ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ababiic...@apache.org
Subject [2/2] ambari git commit: AMBARI-22093 Log Search UI: implement service logs actions functionality. (ababiichuk)
Date Fri, 29 Sep 2017 13:28:45 GMT
AMBARI-22093 Log Search UI: implement service logs actions functionality. (ababiichuk)


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

Branch: refs/heads/trunk
Commit: 8852edd2f5458a22109bc0931fd6e91341d0523f
Parents: 7950e3c
Author: ababiichuk <ababiichuk@hortonworks.com>
Authored: Fri Sep 29 15:45:40 2017 +0300
Committer: ababiichuk <ababiichuk@hortonworks.com>
Committed: Fri Sep 29 15:45:40 2017 +0300

----------------------------------------------------------------------
 .../ambari-logsearch-web/src/app/app.module.ts  |   6 ++
 .../classes/active-service-log-entry.class.ts   |  23 ++++
 .../src/app/classes/list-item.class.ts          |   1 +
 .../queries/audit-logs-query-params.class.ts    |   2 +-
 ...service-logs-truncated-query-params.class.ts |  36 +++++++
 .../classes/service-log-context-entry.class.ts  |  26 +++++
 .../dropdown-button.component.html              |   3 +-
 .../dropdown-button.component.spec.ts           |   5 +-
 .../dropdown-list/dropdown-list.component.html  |   2 +-
 .../dropdown-list.component.spec.ts             |  12 ++-
 .../dropdown-list/dropdown-list.component.ts    |   9 +-
 .../filter-button.component.spec.ts             |   5 +-
 .../filter-dropdown.component.spec.ts           |   5 +-
 .../filters-panel.component.spec.ts             |   5 +-
 .../log-context/log-context.component.html      |  33 ++++++
 .../log-context/log-context.component.less      |  23 ++++
 .../log-context/log-context.component.spec.ts   | 108 +++++++++++++++++++
 .../log-context/log-context.component.ts        |  91 ++++++++++++++++
 .../log-file-entry.component.html               |  20 ++++
 .../log-file-entry.component.less               |  31 ++++++
 .../log-file-entry.component.spec.ts            |  56 ++++++++++
 .../log-file-entry/log-file-entry.component.ts  |  51 +++++++++
 .../logs-container.component.html               |   8 +-
 .../logs-container.component.spec.ts            |   5 +-
 .../logs-container/logs-container.component.ts  |  29 +++--
 .../logs-list/logs-list.component.html          |  30 +++---
 .../logs-list/logs-list.component.less          |  36 +------
 .../logs-list/logs-list.component.spec.ts       |   3 +
 .../components/logs-list/logs-list.component.ts |  34 +++++-
 .../main-container.component.html               |   7 ++
 .../main-container.component.less               |   4 +
 .../main-container.component.spec.ts            |  13 ++-
 .../main-container/main-container.component.ts  |  28 ++++-
 .../menu-button/menu-button.component.spec.ts   |   5 +-
 .../timezone-picker.component.spec.ts           |   5 +-
 .../src/app/components/variables.less           |  30 ++++++
 .../src/app/models/app-state.model.ts           |  10 +-
 .../src/app/models/bar-graph.model.ts           |   2 +-
 .../src/app/models/graph.model.ts               |   2 +-
 .../src/app/models/log.model.ts                 |   1 +
 .../src/app/models/store.model.ts               |  11 ++
 .../services/component-actions.service.spec.ts  |   8 +-
 .../app/services/component-actions.service.ts   |  50 ++++++++-
 .../component-generator.service.spec.ts         |  10 +-
 .../src/app/services/http-client.service.ts     |   7 +-
 .../app/services/logs-container.service.spec.ts |   8 +-
 .../src/app/services/logs-container.service.ts  |  70 ++++++++++--
 .../app/services/storage/reducers.service.ts    |   2 +
 .../storage/service-logs-truncated.service.ts   |  32 ++++++
 .../src/assets/i18n/en.json                     |   7 +-
 50 files changed, 908 insertions(+), 102 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
----------------------------------------------------------------------
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 c4dc698..ff791fe 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
@@ -44,6 +44,7 @@ import {AppStateService} from '@app/services/storage/app-state.service';
 import {AuditLogsService} from '@app/services/storage/audit-logs.service';
 import {ServiceLogsService} from '@app/services/storage/service-logs.service';
 import {ServiceLogsHistogramDataService} from '@app/services/storage/service-logs-histogram-data.service';
+import {ServiceLogsTruncatedService} from '@app/services/storage/service-logs-truncated.service';
 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';
@@ -76,6 +77,8 @@ import {NodeBarComponent} from '@app/components/node-bar/node-bar.component';
 import {SearchBoxComponent} from '@app/components/search-box/search-box.component';
 import {TimeRangePickerComponent} from '@app/components/time-range-picker/time-range-picker.component';
 import {DatePickerComponent} from '@app/components/date-picker/date-picker.component';
+import {LogContextComponent} from '@app/components/log-context/log-context.component';
+import {LogFileEntryComponent} from '@app/components/log-file-entry/log-file-entry.component';
 
 import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe';
 import {TimerSecondsPipe} from '@app/pipes/timer-seconds.pipe';
@@ -124,6 +127,8 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR
     SearchBoxComponent,
     TimeRangePickerComponent,
     DatePickerComponent,
+    LogContextComponent,
+    LogFileEntryComponent,
     TimeZoneAbbrPipe,
     TimerSecondsPipe
   ],
@@ -157,6 +162,7 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR
     AuditLogsService,
     ServiceLogsService,
     ServiceLogsHistogramDataService,
+    ServiceLogsTruncatedService,
     GraphsService,
     HostsService,
     UserConfigsService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/classes/active-service-log-entry.class.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/active-service-log-entry.class.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/active-service-log-entry.class.ts
new file mode 100644
index 0000000..d3d7d95
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/active-service-log-entry.class.ts
@@ -0,0 +1,23 @@
+/**
+ * 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 interface ActiveServiceLogEntry {
+  id: string;
+  host_name: string;
+  component_name: string;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts
index adb023b..1aaaecc 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.class.ts
@@ -22,4 +22,5 @@ export interface ListItem {
   value: any;
   iconClass?: string;
   isChecked?: boolean;
+  action?: string;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.class.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.class.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.class.ts
index e36bf18..3727abb 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.class.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.class.ts
@@ -35,7 +35,7 @@ export class AuditLogsQueryParams extends QueryParams {
   pageSize: string;
   startIndex: string;
   sortBy?: string;
-  sortType?: string;
+  sortType?: 'asc' | 'desc';
   clusters?: string;
   mustBe?: string;
   mustNot?: string;

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.class.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.class.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.class.ts
new file mode 100644
index 0000000..da05cee
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.class.ts
@@ -0,0 +1,36 @@
+/**
+ * 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 {QueryParams} from '@app/classes/queries/query-params.class';
+
+export const defaultParams = {
+  numberRows: '10',
+  scrollType: ''
+};
+
+export class ServiceLogsTruncatedQueryParams extends QueryParams {
+  constructor(options: ServiceLogsTruncatedQueryParams) {
+    const finalParams = Object.assign({}, defaultParams, options);
+    super(finalParams);
+  }
+  id: string;
+  host_name: string;
+  component_name: string;
+  numberRows: string;
+  scrollType: 'before' | 'after' | '';
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-log-context-entry.class.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-log-context-entry.class.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-log-context-entry.class.ts
new file mode 100644
index 0000000..15c05fb
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-log-context-entry.class.ts
@@ -0,0 +1,26 @@
+/**
+ * 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 interface ServiceLogContextEntry {
+  id: string;
+  time: number;
+  level: string;
+  message: string;
+  fileName: string | null;
+  lineNumber: number | null;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
index 9536573..798a609 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
@@ -26,5 +26,6 @@
     <span *ngIf="!hideCaret" class="caret"></span>
   </button>
   <ul data-component="dropdown-list" [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': isRightAlign}"
-      [items]="options" [isMultipleChoice]="isMultipleChoice" (selectedItemChange)="updateValue($event)"></ul>
+      [items]="options" [isMultipleChoice]="isMultipleChoice" (selectedItemChange)="updateValue($event)"
+      [actionArguments]="additionalArgs"></ul>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
index f7227b1..e795986 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
@@ -30,6 +30,7 @@ import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/aud
 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 {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
@@ -56,7 +57,8 @@ describe('DropdownButtonComponent', () => {
           auditLogsFields,
           serviceLogs,
           serviceLogsFields,
-          serviceLogsHistogramData
+          serviceLogsHistogramData,
+          serviceLogsTruncated
         }),
         ...TranslationModules
       ],
@@ -71,6 +73,7 @@ describe('DropdownButtonComponent', () => {
         ServiceLogsService,
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
+        ServiceLogsTruncatedService,
         FilteringService,
         UtilsService,
         ComponentActionsService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
index 316d3f9..5de78ad 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
@@ -26,7 +26,7 @@
     </label>
   </label>
   <span class="list-item-label label-container" *ngIf="!isMultipleChoice"
-        (click)="changeSelectedItem({value: item.value, label: item.label})">
+        (click)="changeSelectedItem(item)">
     <span *ngIf="item.iconClass" [ngClass]="item.iconClass"></span>
     {{item.label | translate}}
     <div #additionalComponent></div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
index eacac04..759a0e1 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
@@ -26,12 +26,15 @@ import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/aud
 import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
 import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
 import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {AppStateService, appState} from '@app/services/storage/app-state.service';
 import {ClustersService, clusters} from '@app/services/storage/clusters.service';
 import {ComponentsService, components} from '@app/services/storage/components.service';
+import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {ComponentGeneratorService} from '@app/services/component-generator.service';
 import {LogsContainerService} from '@app/services/logs-container.service';
 import {HttpClientService} from '@app/services/http-client.service';
 import {FilteringService} from '@app/services/filtering.service';
+import {ComponentActionsService} from '@app/services/component-actions.service';
 
 import {DropdownListComponent} from './dropdown-list.component';
 
@@ -60,8 +63,10 @@ describe('DropdownListComponent', () => {
           serviceLogsFields,
           serviceLogsHistogramData,
           appSettings,
+          appState,
           clusters,
-          components
+          components,
+          serviceLogsTruncated
         })
       ],
       providers: [
@@ -72,6 +77,7 @@ describe('DropdownListComponent', () => {
           useValue: httpClient
         },
         FilteringService,
+        ComponentActionsService,
         HostsService,
         AuditLogsService,
         ServiceLogsService,
@@ -79,8 +85,10 @@ describe('DropdownListComponent', () => {
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
         AppSettingsService,
+        AppStateService,
         ClustersService,
-        ComponentsService
+        ComponentsService,
+        ServiceLogsTruncatedService
       ]
     })
     .compileComponents();

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
index 82656cf..656c901 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
@@ -19,6 +19,7 @@
 import {Component, AfterViewInit, Input, Output, EventEmitter, ViewChildren, ViewContainerRef, QueryList} from '@angular/core';
 import {ListItem} from '@app/classes/list-item.class';
 import {ComponentGeneratorService} from '@app/services/component-generator.service';
+import {ComponentActionsService} from '@app/services/component-actions.service';
 
 @Component({
   selector: 'ul[data-component="dropdown-list"]',
@@ -27,7 +28,7 @@ import {ComponentGeneratorService} from '@app/services/component-generator.servi
 })
 export class DropdownListComponent implements AfterViewInit {
 
-  constructor(private componentGenerator: ComponentGeneratorService) {
+  constructor(private componentGenerator: ComponentGeneratorService, private actions: ComponentActionsService) {
   }
 
   ngAfterViewInit() {
@@ -49,6 +50,9 @@ export class DropdownListComponent implements AfterViewInit {
   @Input()
   additionalLabelComponentSetter?: string;
 
+  @Input()
+  actionArguments: any[] = [];
+
   @Output()
   selectedItemChange: EventEmitter<ListItem> = new EventEmitter();
 
@@ -58,6 +62,9 @@ export class DropdownListComponent implements AfterViewInit {
   containers: QueryList<ViewContainerRef>;
 
   changeSelectedItem(options: ListItem): void {
+    if (options.action) {
+      this.actions[options.action](...this.actionArguments);
+    }
     this.selectedItemChange.emit(options);
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
index a01a3f3..4e6f460 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
@@ -30,6 +30,7 @@ import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/aud
 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 {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
@@ -56,7 +57,8 @@ describe('FilterButtonComponent', () => {
           auditLogsFields,
           serviceLogs,
           serviceLogsFields,
-          serviceLogsHistogramData
+          serviceLogsHistogramData,
+          serviceLogsTruncated
         }),
         ...TranslationModules
       ],
@@ -71,6 +73,7 @@ describe('FilterButtonComponent', () => {
         ServiceLogsService,
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
+        ServiceLogsTruncatedService,
         ComponentActionsService,
         FilteringService,
         UtilsService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
index 85e7ecb..f5b9330 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
@@ -26,6 +26,7 @@ import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/aud
 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 {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
@@ -65,7 +66,8 @@ describe('FilterDropdownComponent', () => {
           auditLogsFields,
           serviceLogs,
           serviceLogsFields,
-          serviceLogsHistogramData
+          serviceLogsHistogramData,
+          serviceLogsTruncated
         }),
         ...TranslationModules
       ],
@@ -77,6 +79,7 @@ describe('FilterDropdownComponent', () => {
         ServiceLogsService,
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
+        ServiceLogsTruncatedService,
         {
           provide: FilteringService,
           useValue: filtering

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
index ae5a4af..0643ea6 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
@@ -30,6 +30,7 @@ import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/aud
 import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
 import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
 import {AppStateService, appState} from '@app/services/storage/app-state.service';
+import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
 import {UtilsService} from '@app/services/utils.service';
@@ -67,7 +68,8 @@ describe('FiltersPanelComponent', () => {
           auditLogsFields,
           serviceLogsFields,
           serviceLogsHistogramData,
-          appState
+          appState,
+          serviceLogsTruncated
         }),
         ...TranslationModules
       ],
@@ -82,6 +84,7 @@ describe('FiltersPanelComponent', () => {
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
         AppStateService,
+        ServiceLogsTruncatedService,
         FilteringService,
         LogsContainerService,
         {

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.html
new file mode 100644
index 0000000..2e51e0b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.html
@@ -0,0 +1,33 @@
+<!--
+  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.
+-->
+
+<modal title="{{hostName}} -> {{componentName}}" submitButtonLabel="modal.close" [showCancelButton]="false"
+       [isLargeModal]="true" (init)="scrollToCurrentEntry()" (submit)="closeLogContext()" (close)="closeLogContext()">
+  <ng-template>
+    <button class="btn btn-primary" (click)="loadBefore()">
+      {{'logs.loadMore' | translate}} <span class="fa fa-arrow-up"></span>
+    </button>
+    <div class="logs">
+      <log-file-entry *ngFor="let log of logs | async" [ngClass]="log.id === id ? currentLogClassName : ''"
+                   [time]="log.time" [level]="log.level" [fileName]="log.fileName" [lineNumber]="log.lineNumber"
+                   [message]="log.message"></log-file-entry>
+    </div>
+    <button class="btn btn-primary" (click)="loadAfter()">
+      {{'logs.loadMore' | translate}} <span class="fa fa-arrow-down"></span>
+    </button>
+  </ng-template>
+</modal>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.less
new file mode 100644
index 0000000..235853b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.less
@@ -0,0 +1,23 @@
+/**
+ * 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';
+
+.logs {
+  max-height: @dropdown-max-height; // TODO implement actual styles
+  overflow-y: auto;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
new file mode 100644
index 0000000..c21750a
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
@@ -0,0 +1,108 @@
+/**
+ * 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 {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} 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';
+import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.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 {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {AppStateService, appState} from '@app/services/storage/app-state.service';
+import {ClustersService, clusters} from '@app/services/storage/clusters.service';
+import {ComponentsService, components} from '@app/services/storage/components.service';
+import {HostsService, hosts} from '@app/services/storage/hosts.service';
+import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
+import {TranslationModules} from '@app/test-config.spec';
+import {ModalComponent} from '@app/components/modal/modal.component';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {HttpClientService} from '@app/services/http-client.service';
+import {FilteringService} from '@app/services/filtering.service';
+
+import {LogContextComponent} from './log-context.component';
+
+describe('LogContextComponent', () => {
+  let component: LogContextComponent;
+  let fixture: ComponentFixture<LogContextComponent>;
+
+  beforeEach(async(() => {
+    const httpClient = {
+      get: () => {
+        return {
+          subscribe: () => {
+          }
+        }
+      }
+    };
+    TestBed.configureTestingModule({
+      declarations: [
+        LogContextComponent,
+        ModalComponent
+      ],
+      imports: [
+        StoreModule.provideStore({
+          auditLogs,
+          serviceLogs,
+          auditLogsFields,
+          serviceLogsFields,
+          serviceLogsHistogramData,
+          appSettings,
+          appState,
+          clusters,
+          components,
+          hosts,
+          serviceLogsTruncated
+        }),
+        ...TranslationModules
+      ],
+      providers: [
+        AuditLogsService,
+        ServiceLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
+        AppSettingsService,
+        AppStateService,
+        ClustersService,
+        ComponentsService,
+        HostsService,
+        ServiceLogsTruncatedService,
+        LogsContainerService,
+        {
+          provide: HttpClientService,
+          useValue: httpClient
+        },
+        FilteringService
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LogContextComponent);
+    component = fixture.componentInstance;
+    component.scrollToCurrentEntry = () => {};
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.ts
new file mode 100644
index 0000000..467de98
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.ts
@@ -0,0 +1,91 @@
+/**
+ * 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, Input, ElementRef} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {ServiceLogsTruncatedService} from '@app/services/storage/service-logs-truncated.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
+import {ServiceLog} from '@app/models/service-log.model';
+import {ServiceLogContextEntry} from '@app/classes/service-log-context-entry.class';
+
+@Component({
+  selector: 'log-context',
+  templateUrl: './log-context.component.html',
+  styleUrls: ['./log-context.component.less']
+})
+export class LogContextComponent {
+
+  constructor(private element: ElementRef, private logsContainer: LogsContainerService, private serviceLogsTruncatedStorage: ServiceLogsTruncatedService, private appState: AppStateService) {
+  }
+
+  @Input()
+  id: string;
+
+  @Input()
+  hostName: string;
+
+  @Input()
+  componentName: string;
+
+  readonly currentLogClassName: string = 'alert-warning'; // TODO implement custom class name with actual styles
+
+  firstEntryId: string;
+
+  lastEntryId: string;
+
+  logs: Observable<ServiceLogContextEntry[]> = this.serviceLogsTruncatedStorage.getAll().map((logs: ServiceLog[]): ServiceLogContextEntry[] => {
+    if (logs.length) {
+      this.firstEntryId = logs[0].id;
+      this.lastEntryId = logs[logs.length - 1].id;
+    }
+    return logs.map((log: ServiceLog): ServiceLogContextEntry => {
+      return {
+        id: log.id,
+        time: log.logtime,
+        level: log.level,
+        message: log.log_message,
+        fileName: log.file,
+        lineNumber: log.line_number
+      };
+    });
+  });
+
+  closeLogContext(): void {
+    this.appState.setParameters({
+      isServiceLogContextView: false,
+      activeLog: null
+    });
+    this.serviceLogsTruncatedStorage.clear();
+    this.firstEntryId = '';
+    this.lastEntryId = '';
+  }
+
+  scrollToCurrentEntry() {
+    this.element.nativeElement.getElementsByClassName(this.currentLogClassName).item(0).scrollIntoView();
+  }
+
+  loadBefore(): void {
+    this.logsContainer.loadLogContext(this.firstEntryId, this.hostName, this.componentName, 'before');
+  }
+
+  loadAfter(): void {
+    this.logsContainer.loadLogContext(this.lastEntryId, this.hostName, this.componentName, 'after');
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.html
new file mode 100644
index 0000000..7d4c296
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.html
@@ -0,0 +1,20 @@
+<!--
+  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 class="log">{{time | amTz: timeZone |amDateFormat: timeFormat}} <span
+  class="{{'log-level ' + level.toLowerCase()}}">{{level}}</span><span *ngIf="fileName"> {{fileName}}<span
+  *ngIf="lineNumber">:{{lineNumber}}</span></span> - {{message}}</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.less
new file mode 100644
index 0000000..d3523d3
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.less
@@ -0,0 +1,31 @@
+/**
+ * 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 {
+  display: block;
+
+  .log {
+    font-family: monospace;
+    white-space: pre-wrap;
+
+    .log-level {
+      .log-colors;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.spec.ts
new file mode 100644
index 0000000..0ae7e67
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.spec.ts
@@ -0,0 +1,56 @@
+/**
+ * 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {MomentModule} from 'angular2-moment';
+import {MomentTimezoneModule} from 'angular-moment-timezone';
+import {StoreModule} from '@ngrx/store';
+import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+
+import {LogFileEntryComponent} from './log-file-entry.component';
+
+describe('LogFileEntryComponent', () => {
+  let component: LogFileEntryComponent;
+  let fixture: ComponentFixture<LogFileEntryComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [LogFileEntryComponent],
+      imports: [
+        StoreModule.provideStore({
+          appSettings
+        }),
+        MomentModule,
+        MomentTimezoneModule
+      ],
+      providers: [
+        AppSettingsService
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LogFileEntryComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.ts
new file mode 100644
index 0000000..c0a7393
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-file-entry/log-file-entry.component.ts
@@ -0,0 +1,51 @@
+/**
+ * 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, Input} from '@angular/core';
+import {AppSettingsService} from '@app/services/storage/app-settings.service';
+
+@Component({
+  selector: 'log-file-entry',
+  templateUrl: './log-file-entry.component.html',
+  styleUrls: ['./log-file-entry.component.less']
+})
+export class LogFileEntryComponent {
+
+  constructor(private appSettings: AppSettingsService) {
+    appSettings.getParameter('timeZone').subscribe((value: string) => this.timeZone = value);
+  }
+
+  @Input()
+  time: string = '';
+
+  @Input()
+  level: string = '';
+
+  @Input()
+  fileName?: string;
+
+  @Input()
+  lineNumber?: string;
+
+  @Input()
+  message: string = '';
+
+  readonly timeFormat: string = 'YYYY-MM-DD HH:mm:ss,SSS';
+
+  timeZone: string;
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
index 5145b76..9c6c336 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
@@ -21,7 +21,9 @@
   </div>
 </div>
 <time-histogram class="col-md-12" [data]="histogramData" [customOptions]="histogramOptions"></time-histogram>
-<dropdown-button class="pull-right" label="logs.columns" [options]="availableColumns | async" [isRightAlign]="true"
-                 isMultipleChoice="true" action="updateSelectedColumns"
-                 [additionalArgs]="logsTypeMapObject.fieldsModel"></dropdown-button>
+<dropdown-button *ngIf="!isServiceLogsFileView" class="pull-right" label="logs.columns"
+                 [options]="availableColumns | async" [isRightAlign]="true" [isMultipleChoice]="true"
+                 action="updateSelectedColumns" [additionalArgs]="logsTypeMapObject.fieldsModel"></dropdown-button>
 <logs-list [logs]="logs | async" [totalCount]="totalCount" [displayedColumns]="displayedColumns"></logs-list>
+<log-context *ngIf="isServiceLogContextView" [hostName]="activeLog.host_name" [componentName]="activeLog.component_name"
+             [id]="activeLog.id"></log-context>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
index 811c6e6..f3b28d1 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
@@ -30,6 +30,7 @@ import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-log
 import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
 import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
 import {HostsService, hosts} from '@app/services/storage/hosts.service';
+import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {HttpClientService} from '@app/services/http-client.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
@@ -63,7 +64,8 @@ describe('LogsContainerComponent', () => {
           serviceLogs,
           serviceLogsFields,
           serviceLogsHistogramData,
-          hosts
+          hosts,
+          serviceLogsTruncated
         }),
         ...TranslationModules
       ],
@@ -82,6 +84,7 @@ describe('LogsContainerComponent', () => {
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
         HostsService,
+        ServiceLogsTruncatedService,
         FilteringService,
         UtilsService,
         LogsContainerService

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
----------------------------------------------------------------------
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 b1fad17..fd3a58b 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
@@ -27,6 +27,7 @@ import {AppStateService} from '@app/services/storage/app-state.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';
+import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry.class';
 
 @Component({
   selector: 'logs-container',
@@ -37,12 +38,13 @@ export class LogsContainerComponent implements OnInit {
 
   constructor(private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: AppStateService, private filtering: FilteringService, private logsContainer: LogsContainerService) {
     serviceLogsHistogramStorage.getAll().subscribe(data => this.histogramData = this.logsContainer.getHistogramData(data));
+    appState.getParameter('isServiceLogContextView').subscribe((value: boolean) => this.isServiceLogContextView = value);
   }
 
   ngOnInit() {
     const fieldsModel = this.logsTypeMapObject.fieldsModel,
       logsModel = this.logsTypeMapObject.logsModel;
-    this.appState.getParameter(this.logsTypeMapObject.isSetFlag).subscribe(value => this.isLogsSet = value);
+    this.appState.getParameter(this.logsTypeMapObject.isSetFlag).subscribe((value: boolean) => this.isLogsSet = value);
     this.availableColumns = fieldsModel.getAll().map(fields => {
       return fields.filter(field => field.isAvailable).map(field => {
         return {
@@ -56,15 +58,13 @@ export class LogsContainerComponent implements OnInit {
       const availableFields = columns.filter(field => field.isAvailable),
         availableNames = availableFields.map(field => field.name);
       if (availableNames.length && !this.isLogsSet) {
-        this.logs = 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;
-        }));
+        this.logs = logsModel.getAll().map((logs: (AuditLog | ServiceLog)[]): (AuditLog | ServiceLog)[] => {
+          return logs.map((log: AuditLog | ServiceLog): AuditLog | ServiceLog => {
+            return availableNames.reduce((obj, key) => Object.assign(obj, {
+              [key]: log[key]
+            }), {});
+          });
+        });
         this.appState.setParameter(this.logsTypeMapObject.isSetFlag, true);
       }
       this.displayedColumns = columns.filter(column => column.isAvailable && column.isDisplayed);
@@ -112,4 +112,13 @@ export class LogsContainerComponent implements OnInit {
     };
   }
 
+  isServiceLogContextView: boolean = false;
+
+  get isServiceLogsFileView(): boolean {
+    return this.logsContainer.isServiceLogsFileView;
+  };
+
+  get activeLog(): ActiveServiceLogEntry | null {
+    return this.logsContainer.activeLog;
+  };
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
index 2942b20..b27eb69 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
@@ -19,20 +19,20 @@
   <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 && logs.length">
-  <div class="logs-header">
-    <div class="col-md-1">{{'logs.status' | translate}}</div>
-    <div class="col-md-11">{{'logs.details' | translate}}</div>
-  </div>
-</div>
 <div *ngFor="let log of logs; let i = index">
-  <div *ngIf="i === 0 || isDifferentDates(log.logtime, logs[i - 1].logtime)" class="col-md-12">
+  <div *ngIf="!isServiceLogsFileView && (i === 0 || isDifferentDates(log.logtime, logs[i - 1].logtime))" class="col-md-12">
     <div class="logs-header">{{log.logtime | amTz: timeZone | amDateFormat: dateFormat}}</div>
   </div>
-  <accordion-panel [toggleId]="'details-' + i" class="col-md-12">
+  <accordion-panel *ngIf="!isServiceLogsFileView" [toggleId]="'details-' + i" class="col-md-12">
     <ng-template>
-      <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('level')" [ngClass]="'hexagon ' + log.level.toLowerCase()"></div>
+      <div class="col-md-1">
+        <dropdown-button iconClass="fa fa-ellipsis-h" [hideCaret]="true" [options]="logActions"
+                         [additionalArgs]="[log]"></dropdown-button>
+      </div>
+      <div *ngIf="isColumnDisplayed('level')" [ngClass]="'col-md-1 log-status ' + log.level.toLowerCase()">
+        {{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">
@@ -41,9 +41,7 @@
       </div>
       <div class="col-md-6 log-content-wrapper">
         <div class="collapse log-actions" attr.id="details-{{i}}">
-          <span class="action-icon fa fa-search"></span>
-          <span class="action-icon fa fa-external-link"></span>
-          <span class="action-icon fa fa-crosshairs"></span>
+          <!-- TODO remove after restyling the table -->
         </div>
         <div class="log-content-inner-wrapper">
           <div class="log-content" *ngIf="isColumnDisplayed('log_message')"
@@ -58,8 +56,10 @@
       </div>
     </ng-template>
   </accordion-panel>
+  <log-file-entry *ngIf="isServiceLogsFileView" class="col-md-12" [time]="log.logtime" [level]="log.level"
+                  [fileName]="log.file" [lineNumber]="log.line_number" [message]="log.log_message"></log-file-entry>
 </div>
-<ul #contextmenu data-component="dropdown-list" class="dropdown-menu context-menu" [items]="contextMenuItems"
-    (selectedItemChange)="updateQuery($event)"></ul>
+<ul #contextmenu *ngIf="!isServiceLogsFileView" data-component="dropdown-list" class="dropdown-menu context-menu"
+    [items]="contextMenuItems" (selectedItemChange)="updateQuery($event)"></ul>
 <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/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
index 577043f..0fded67 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
@@ -30,7 +30,7 @@
 }
 
 .hexagon {
-  // TODO get rid of magic numbers, base on actual design
+  // TODO remove, since it's not a part of updated design
   left: -7.5px;
 
   &.fatal {
@@ -64,34 +64,7 @@
 
 .log-status {
   text-transform: uppercase;
-
-  &.fatal {
-    color: @fatal-color;
-  }
-
-  &.error {
-    color: @error-color;
-  }
-
-  &.warn {
-    color: @warning-color;
-  }
-
-  &.info {
-    color: @info-color;
-  }
-
-  &.debug {
-    color: @debug-color;
-  }
-
-  &.trace {
-    color: @trace-color;
-  }
-
-  &.unknown {
-    color: @unknown-color;
-  }
+  .log-colors;
 }
 
 .log-type {
@@ -117,11 +90,6 @@
   }
 
   .log-actions {
-    position: absolute;
-    right: 40px;
-    top: 0;
-    border: @input-border;
-
     &.collapsing + .log-content-inner-wrapper, &.collapse.in + .log-content-inner-wrapper {
       min-height: 6em;
       max-height: none;

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
index 8c67a13..8ee4ca3 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
@@ -24,6 +24,7 @@ import {MomentTimezoneModule} from 'angular-moment-timezone';
 import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
 import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
 import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {AppStateService, appState} from '@app/services/storage/app-state.service';
 import {ClustersService, clusters} from '@app/services/storage/clusters.service';
 import {ComponentsService, components} from '@app/services/storage/components.service';
 import {HostsService, hosts} from '@app/services/storage/hosts.service';
@@ -53,6 +54,7 @@ describe('LogsListComponent', () => {
           auditLogs,
           serviceLogs,
           appSettings,
+          appState,
           clusters,
           components,
           hosts
@@ -69,6 +71,7 @@ describe('LogsListComponent', () => {
         AuditLogsService,
         ServiceLogsService,
         AppSettingsService,
+        AppStateService,
         ClustersService,
         ComponentsService,
         HostsService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
index aeb55da..c94b967 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
@@ -15,28 +15,32 @@
  * limitations under the License.
  */
 
-import {Component, OnInit, Input, ViewChild, ElementRef} from '@angular/core';
+import {Component, AfterViewInit, Input, ViewChild, ElementRef} from '@angular/core';
 import {FormGroup} from '@angular/forms';
 import 'rxjs/add/operator/map';
+import {AppStateService} from '@app/services/storage/app-state.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
+import {AuditLog} from '@app/models/audit-log.model';
+import {ServiceLog} from '@app/models/service-log.model';
 
 @Component({
   selector: 'logs-list',
   templateUrl: './logs-list.component.html',
   styleUrls: ['./logs-list.component.less']
 })
-export class LogsListComponent implements OnInit {
+export class LogsListComponent implements AfterViewInit {
 
-  constructor(private filtering: FilteringService, private utils: UtilsService) {
+  constructor(private filtering: FilteringService, private utils: UtilsService, private appState: AppStateService) {
+    appState.getParameter('isServiceLogsFileView').subscribe((value: boolean) => this.isServiceLogsFileView = value);
   }
 
-  ngOnInit() {
+  ngAfterViewInit() {
     this.contextMenuElement = this.contextMenu.nativeElement;
   }
 
   @Input()
-  logs: any[] = [];
+  logs: (AuditLog| ServiceLog)[] = [];
 
   @Input()
   totalCount: number = 0;
@@ -70,6 +74,24 @@ export class LogsListComponent implements OnInit {
     }
   ];
 
+  readonly logActions = [
+    {
+      label: 'logs.copy',
+      iconClass: 'fa fa-files-o',
+      action: 'copyLog'
+    },
+    {
+      label: 'logs.open',
+      iconClass: 'fa fa-external-link',
+      action: 'openLog'
+    },
+    {
+      label: 'logs.context',
+      iconClass: 'fa fa-crosshairs',
+      action: 'openContext'
+    }
+  ];
+
   readonly dateFormat: string = 'dddd, MMMM Do';
 
   readonly timeFormat: string = 'h:mm:ss A';
@@ -86,6 +108,8 @@ export class LogsListComponent implements OnInit {
     return this.filtering.filtersForm;
   }
 
+  isServiceLogsFileView: boolean = false;
+
   isDifferentDates(dateA, dateB): boolean {
     return this.utils.isDifferentDates(dateA, dateB, this.timeZone);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
index 69b3887..7e3621a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
@@ -20,5 +20,12 @@
   <span class="fa fa-spinner fa-spin"></span>
 </div>
 <login-form *ngIf="!isInitialLoading && !isAuthorized"></login-form>
+
+<!-- TODO implement tabs: Service Logs/Audit Logs/active file -->
+<div *ngIf="isServiceLogsFileView" class="col-md-12 logs-header">
+  {{activeLogHostName}} &gt;&gt; {{activeLogComponentName}}
+  <span class="fa fa-times close-icon" (click)="closeLog()"></span>
+</div>
+
 <filters-panel *ngIf="isAuthorized" class="row"></filters-panel>
 <logs-container *ngIf="isAuthorized" logsType="serviceLogs"></logs-container>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
index 9736628..f7dcc05 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
@@ -21,4 +21,8 @@
 :host {
   .full-size;
   overflow-x: hidden;
+
+  .close-icon {
+    .clickable-item;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
index 42fba68..bbbebdf 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
@@ -32,6 +32,14 @@ describe('MainContainerComponent', () => {
   let fixture: ComponentFixture<MainContainerComponent>;
 
   beforeEach(async(() => {
+    const httpClient = {
+      get: () => {
+        return {
+          subscribe: () => {
+          }
+        }
+      }
+    };
     TestBed.configureTestingModule({
       declarations: [MainContainerComponent],
       imports: [
@@ -47,7 +55,10 @@ describe('MainContainerComponent', () => {
         AppStateService,
         AuditLogsFieldsService,
         ServiceLogsFieldsService,
-        HttpClientService
+        {
+          provide: HttpClientService,
+          useValue: httpClient
+        }
       ]
     })
     .compileComponents();

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
index 53d58cf..32fe1cf 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
@@ -23,6 +23,7 @@ import {AuditLogsFieldsService} from '@app/services/storage/audit-logs-fields.se
 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';
+import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry.class';
 
 @Component({
   selector: 'main-container',
@@ -33,8 +34,18 @@ export class MainContainerComponent {
 
   constructor(private httpClient: HttpClientService, private appState: AppStateService, private auditLogsFieldsStorage: AuditLogsFieldsService, private serviceLogsFieldsStorage: ServiceLogsFieldsService) {
     this.loadColumnsNames();
-    appState.getParameter('isAuthorized').subscribe(value => this.isAuthorized = value);
-    appState.getParameter('isInitialLoading').subscribe(value => this.isInitialLoading = value);
+    appState.getParameter('isAuthorized').subscribe((value: boolean) => this.isAuthorized = value);
+    appState.getParameter('isInitialLoading').subscribe((value: boolean) => this.isInitialLoading = value);
+    appState.getParameter('isServiceLogsFileView').subscribe((value: boolean) => this.isServiceLogsFileView = value);
+    appState.getParameter('activeLog').subscribe((value: ActiveServiceLogEntry | null) => {
+      if (value) {
+        this.activeLogHostName = value.host_name;
+        this.activeLogComponentName = value.component_name;
+      } else {
+        this.activeLogHostName = '';
+        this.activeLogComponentName = '';
+      }
+    });
   }
 
   @ContentChild(TemplateRef)
@@ -44,6 +55,12 @@ export class MainContainerComponent {
 
   isInitialLoading: boolean = false;
 
+  isServiceLogsFileView: boolean = false;
+
+  activeLogHostName: string = '';
+
+  activeLogComponentName: string = '';
+
   private loadColumnsNames(): void {
     this.httpClient.get('serviceLogsFields').subscribe(response => {
       const jsonResponse = response.json();
@@ -63,4 +80,11 @@ export class MainContainerComponent {
     return Object.keys(keysObject).map(key => new fieldClass(key));
   }
 
+  closeLog(): void {
+    this.appState.setParameters({
+      isServiceLogsFileView: false,
+      activeLog: null
+    });
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
index f92961e..5414f4f 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
@@ -30,6 +30,7 @@ import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/aud
 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 {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
@@ -55,7 +56,8 @@ describe('MenuButtonComponent', () => {
           auditLogsFields,
           serviceLogs,
           serviceLogsFields,
-          serviceLogsHistogramData
+          serviceLogsHistogramData,
+          serviceLogsTruncated
         }),
         ...TranslationModules
       ],
@@ -70,6 +72,7 @@ describe('MenuButtonComponent', () => {
         ServiceLogsService,
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
+        ServiceLogsTruncatedService,
         ComponentActionsService,
         FilteringService,
         HttpClientService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts
----------------------------------------------------------------------
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 7d1e907..7105624 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
@@ -29,6 +29,7 @@ import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/aud
 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 {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
@@ -60,7 +61,8 @@ describe('TimeZonePickerComponent', () => {
           auditLogsFields,
           serviceLogs,
           serviceLogsFields,
-          serviceLogsHistogramData
+          serviceLogsHistogramData,
+          serviceLogsTruncated
         }),
         ...TranslationModules
       ],
@@ -75,6 +77,7 @@ describe('TimeZonePickerComponent', () => {
         ServiceLogsService,
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
+        ServiceLogsTruncatedService,
         ComponentActionsService,
         FilteringService,
         HttpClientService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
----------------------------------------------------------------------
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 88b0c91..2dc6278 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
@@ -138,3 +138,33 @@
     background-color: #F5F5F5;
   }
 }
+
+.log-colors {
+  &.fatal {
+    color: @fatal-color;
+  }
+
+  &.error {
+    color: @error-color;
+  }
+
+  &.warn {
+    color: @warning-color;
+  }
+
+  &.info {
+    color: @info-color;
+  }
+
+  &.debug {
+    color: @debug-color;
+  }
+
+  &.trace {
+    color: @trace-color;
+  }
+
+  &.unknown {
+    color: @unknown-color;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/models/app-state.model.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/models/app-state.model.ts b/ambari-logsearch/ambari-logsearch-web/src/app/models/app-state.model.ts
index 28ae763..267bf15 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/models/app-state.model.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/models/app-state.model.ts
@@ -16,6 +16,8 @@
  * limitations under the License.
  */
 
+import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry.class';
+
 export interface AppState {
   isAuthorized: boolean;
   isInitialLoading: boolean;
@@ -23,6 +25,9 @@ export interface AppState {
   isAuditLogsSet: boolean;
   isServiceLogsSet: boolean;
   activeLogsType?: string;
+  isServiceLogsFileView: boolean;
+  isServiceLogContextView: boolean;
+  activeLog: ActiveServiceLogEntry | null;
 }
 
 export const initialState: AppState = {
@@ -31,5 +36,8 @@ export const initialState: AppState = {
   isLoginInProgress: false,
   isAuditLogsSet: false,
   isServiceLogsSet: false,
-  activeLogsType: 'serviceLogs' // TODO implement setting the parameter depending on user's navigation
+  activeLogsType: 'serviceLogs', // TODO implement setting the parameter depending on user's navigation
+  isServiceLogsFileView: false,
+  isServiceLogContextView: false,
+  activeLog: null
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/models/bar-graph.model.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/models/bar-graph.model.ts b/ambari-logsearch/ambari-logsearch-web/src/app/models/bar-graph.model.ts
index a197bf5..6c9a049 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/models/bar-graph.model.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/models/bar-graph.model.ts
@@ -19,6 +19,6 @@
 import {CommonEntry} from '@app/models/common-entry.model';
 
 export interface BarGraph {
-  dataCount: CommonEntry[],
+  dataCount: CommonEntry[];
   name: string;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/models/graph.model.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/models/graph.model.ts b/ambari-logsearch/ambari-logsearch-web/src/app/models/graph.model.ts
index 04966b2..be31f19 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/models/graph.model.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/models/graph.model.ts
@@ -19,5 +19,5 @@
 export interface Graph {
   name: string;
   count: string;
-  dataList?: Graph[]
+  dataList?: Graph[];
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/models/log.model.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/models/log.model.ts b/ambari-logsearch/ambari-logsearch-web/src/app/models/log.model.ts
index 188bbd2..c598e41 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/models/log.model.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/models/log.model.ts
@@ -26,6 +26,7 @@ export interface Log {
   case_id?: string;
   log_message: string;
   logfile_line_number: number;
+  line_number?: number;
   message_md5: string;
   cluster: string;
   event_count: number;

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/models/store.model.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/models/store.model.ts b/ambari-logsearch/ambari-logsearch-web/src/app/models/store.model.ts
index 31d52b3..a6a084f 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/models/store.model.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/models/store.model.ts
@@ -32,6 +32,7 @@ import {ServiceLogField} from '@app/models/service-log-field.model';
 
 export const storeActions = {
   'ARRAY.ADD': 'ADD',
+  'ARRAY.ADD.START': 'ADD_TO_START',
   'ARRAY.DELETE.PRIMITIVE': 'DELETE_PRIMITIVE',
   'ARRAY.DELETE.OBJECT': 'DELETE_OBJECT',
   'ARRAY.CLEAR': 'CLEAR',
@@ -46,6 +47,7 @@ export interface AppStore {
   auditLogs: AuditLog[];
   serviceLogs: ServiceLog[];
   serviceLogsHistogramData: BarGraph[];
+  serviceLogsTruncated: ServiceLog[];
   graphs: Graph[];
   hosts: Node[];
   userConfigs: UserConfig[];
@@ -86,6 +88,13 @@ export class CollectionModelService extends ModelService {
     });
   }
 
+  addInstancesToStart(instances: any[]): void {
+    this.store.dispatch({
+      type: `${storeActions['ARRAY.ADD.START']}_${this.modelName}`,
+      payload: instances
+    });
+  }
+
   deleteObjectInstance(instance: any): void {
     this.store.dispatch({
       type: `${storeActions['ARRAY.DELETE.OBJECT']}_${this.modelName}`,
@@ -143,6 +152,8 @@ export function getCollectionReducer(modelName: string, defaultState: any = []):
     switch (action.type) {
       case `${storeActions['ARRAY.ADD']}_${modelName}`:
         return [...state, ...action.payload];
+      case `${storeActions['ARRAY.ADD.START']}_${modelName}`:
+        return [...action.payload, ...state];
       case `${storeActions['ARRAY.DELETE.OBJECT']}_${modelName}`:
         return state.filter(instance => instance.id !== action.payload.id);
       case `${storeActions['ARRAY.DELETE.PRIMITIVE']}_${modelName}`:

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
index e737155..3dbd992 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
@@ -19,6 +19,7 @@
 import {TestBed, inject} from '@angular/core/testing';
 import {StoreModule} from '@ngrx/store';
 import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {AppStateService, appState} from '@app/services/storage/app-state.service';
 import {ClustersService, clusters} from '@app/services/storage/clusters.service';
 import {ComponentsService, components} from '@app/services/storage/components.service';
 import {HostsService, hosts} from '@app/services/storage/hosts.service';
@@ -27,6 +28,7 @@ import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-log
 import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.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 {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
 import {LogsContainerService} from '@app/services/logs-container.service';
@@ -48,6 +50,7 @@ describe('ComponentActionsService', () => {
       imports: [
         StoreModule.provideStore({
           appSettings,
+          appState,
           clusters,
           components,
           hosts,
@@ -55,12 +58,14 @@ describe('ComponentActionsService', () => {
           serviceLogs,
           auditLogsFields,
           serviceLogsFields,
-          serviceLogsHistogramData
+          serviceLogsHistogramData,
+          serviceLogsTruncated
         })
       ],
       providers: [
         ComponentActionsService,
         AppSettingsService,
+        AppStateService,
         ClustersService,
         ComponentsService,
         HostsService,
@@ -69,6 +74,7 @@ describe('ComponentActionsService', () => {
         AuditLogsFieldsService,
         ServiceLogsFieldsService,
         ServiceLogsHistogramDataService,
+        ServiceLogsTruncatedService,
         FilteringService,
         {
           provide: HttpClientService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/8852edd2/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
index dba0f8f..b3ff0b0 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
@@ -18,14 +18,16 @@
 
 import {Injectable} from '@angular/core';
 import {AppSettingsService} from '@app/services/storage/app-settings.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
 import {CollectionModelService} from '@app/models/store.model';
 import {FilteringService} from '@app/services/filtering.service';
 import {LogsContainerService} from '@app/services/logs-container.service';
+import {ServiceLog} from '@app/models/service-log.model';
 
 @Injectable()
 export class ComponentActionsService {
 
-  constructor(private appSettings: AppSettingsService, private filtering: FilteringService, private logsContainer: LogsContainerService) {
+  constructor(private appSettings: AppSettingsService, private appState: AppStateService, private filtering: FilteringService, private logsContainer: LogsContainerService) {
   }
 
   //TODO implement actions
@@ -44,6 +46,52 @@ export class ComponentActionsService {
   openHistory() {
   }
 
+  copyLog(log: ServiceLog): void {
+    if (document.queryCommandSupported('copy')) {
+      const text = log.log_message,
+        node = document.createElement('textarea');
+      node.value = text;
+      Object.assign(node.style, {
+        position: 'fixed',
+        top: '0',
+        left: '0',
+        width: '1px',
+        height: '1px',
+        border: 'none',
+        outline: 'none',
+        boxShadow: 'none',
+        backgroundColor: 'transparent',
+        padding: '0'
+      });
+      document.body.appendChild(node);
+      node.select();
+      if (document.queryCommandEnabled('copy')) {
+        document.execCommand('copy');
+      } else {
+        // TODO open failed alert
+      }
+      // TODO success alert
+      document.body.removeChild(node);
+    } else {
+      // TODO failed alert
+    }
+  }
+
+  openLog(log: ServiceLog): void {
+    this.appState.setParameters({
+      isServiceLogsFileView: true,
+      activeLog: {
+        id: log.id,
+        host_name: log.host,
+        component_name: log.type
+      }
+    });
+  }
+
+  openContext(log: ServiceLog): void {
+    this.logsContainer.loadLogContext(log.id, log.host, log.type);
+  }
+
   startCapture(): void {
     this.filtering.startCaptureTimer();
   }


Mime
View raw message