You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2017/11/13 16:57:51 UTC
[34/51] [abbrv] ambari git commit: AMBARI-22388 Log Search UI:
restyle logs list. (Istvan Tobias via ababiichuk)
AMBARI-22388 Log Search UI: restyle logs list. (Istvan Tobias via ababiichuk)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/38476f7a
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/38476f7a
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/38476f7a
Branch: refs/heads/branch-feature-AMBARI-21674
Commit: 38476f7a1a7a371fd0f57af2f931427f1e276d27
Parents: 5f714ce
Author: Istvan Tobias <to...@gmail.com>
Authored: Thu Nov 9 13:24:16 2017 +0200
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Thu Nov 9 13:24:16 2017 +0200
----------------------------------------------------------------------
.../ambari-logsearch-web/src/app/app.module.ts | 4 +
.../log-level/log-level.component.html | 18 +++
.../log-level/log-level.component.spec.ts | 73 +++++++++++
.../components/log-level/log-level.component.ts | 52 ++++++++
.../log-message/log-message.component.html | 24 ++++
.../log-message/log-message.component.less | 69 ++++++++++
.../log-message/log-message.component.spec.ts | 64 +++++++++
.../log-message/log-message.component.ts | 129 ++++++++++++++++++
.../logs-list/logs-list.component.html | 92 +++++++------
.../logs-list/logs-list.component.less | 130 +++++++++----------
.../ambari-logsearch-web/webpack.config.js | 15 ++-
11 files changed, 550 insertions(+), 120 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/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 488437e..56562df 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
@@ -68,6 +68,8 @@ import {FilterButtonComponent} from '@app/components/filter-button/filter-button
import {AccordionPanelComponent} from '@app/components/accordion-panel/accordion-panel.component';
import {CollapsiblePanelComponent} from '@app/components/collapsible-panel/collapsible-panel.component';
import {LogsListComponent} from '@app/components/logs-list/logs-list.component';
+import {LogMessageComponent} from '@app/components/log-message/log-message.component';
+import {LogLevelComponent} from '@app/components/log-level/log-level.component';
import {DropdownButtonComponent} from '@app/components/dropdown-button/dropdown-button.component';
import {PaginationComponent} from '@app/components/pagination/pagination.component';
import {PaginationControlsComponent} from '@app/components/pagination-controls/pagination-controls.component';
@@ -121,6 +123,8 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR
AccordionPanelComponent,
CollapsiblePanelComponent,
LogsListComponent,
+ LogLevelComponent,
+ LogMessageComponent,
DropdownButtonComponent,
PaginationComponent,
PaginationControlsComponent,
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.html
new file mode 100644
index 0000000..d72c9d33
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.html
@@ -0,0 +1,18 @@
+<!--
+ 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.
+-->
+<i class="fa {{cssClass}}"></i>
+{{logEntry.level}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.ts
new file mode 100644
index 0000000..c13d373
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.ts
@@ -0,0 +1,73 @@
+/**
+ * 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 {DebugElement} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {LogLevelComponent} from './log-level.component';
+import {By} from '@angular/platform-browser';
+
+describe('LogLevelComponent', () => {
+ let component: LogLevelComponent;
+ let fixture: ComponentFixture<LogLevelComponent>;
+ let de: DebugElement;
+ let el: HTMLElement;
+ let logLevelMap = {
+ warn: 'fa-exclamation-triangle',
+ fatal: 'fa-exclamation-circle',
+ error: 'fa-exclamation-circle',
+ info: 'fa-info-circle',
+ debug: 'fa-bug',
+ trace: 'fa-random',
+ unknown: 'fa-question-circle'
+ };
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ LogLevelComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LogLevelComponent);
+ component = fixture.componentInstance;
+ component.logEntry = {level: 'unknown'};
+ fixture.detectChanges();
+ de = fixture.debugElement.query(By.css('i.fa'));
+ el = de.nativeElement;
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ Object.keys(logLevelMap).forEach((level) => {
+ describe(level, () => {
+ beforeEach(() => {
+ component.logEntry = {level: level};
+ fixture.detectChanges();
+ });
+ it(`should return with the ${logLevelMap[level]} css class for ${level} log level`, () => {
+ expect(component.cssClass).toEqual(logLevelMap[level]);
+ });
+ it(`should set the ${logLevelMap[level]} css class on the icon element`, () => {
+ expect(el.classList).toContain(logLevelMap[level]);
+ });
+ });
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts
new file mode 100644
index 0000000..8542770
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts
@@ -0,0 +1,52 @@
+/**
+ * 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';
+
+/**
+ * This is a simple UI component to display the log message. The goal is to be able to show one line and be collapsile
+ * to show the full log message with new lines.
+ * @class LogMessageComponent
+ */
+@Component({
+ selector: 'log-level',
+ templateUrl: './log-level.component.html',
+ styleUrls: []
+})
+export class LogLevelComponent {
+
+ /**
+ * This is the log entry object
+ * @type {object}
+ */
+ @Input()
+ logEntry: any;
+
+ private classMap: object = {
+ warn: 'fa-exclamation-triangle',
+ fatal: 'fa-exclamation-circle',
+ error: 'fa-exclamation-circle',
+ info: 'fa-info-circle',
+ debug: 'fa-bug',
+ trace: 'fa-random',
+ unknown: 'fa-question-circle'
+ };
+
+ get cssClass() {
+ return this.classMap[((this.logEntry && this.logEntry.level) || 'unknown').toLowerCase()];
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html
new file mode 100644
index 0000000..d4c2902
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html
@@ -0,0 +1,24 @@
+<!--
+ 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 [ngClass]="{
+ 'log-message-container': true,
+ 'log-message-container-collapsible': addCaret,
+ 'log-message-container-open': isOpen
+ }">
+ <button *ngIf="addCaret" (click)="onCaretClick($event)"><i class="caret"></i></button>
+ <div #content class="log-message-content"><ng-content></ng-content></div>
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less
new file mode 100644
index 0000000..602d7bd
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less
@@ -0,0 +1,69 @@
+/**
+ * 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 {
+ .log-message-container {
+ display: block;
+ margin: 0;
+ padding: 0;
+
+ .caret {
+ margin-top: -3px;
+ transition: transform 250ms;
+ transform: rotate(-90deg);
+ }
+ &.log-message-container-open .caret {
+ transform: rotate(0deg);
+ }
+
+ .log-message-content {
+ max-height: calc(20em/14); // from Bootstrap
+ overflow: hidden;
+ padding-left: 1em;
+ position: relative;
+ }
+ &.log-message-container-open .log-message-content {
+ max-height: none;
+ white-space: pre-wrap;
+ &:before {
+ display: none;
+ }
+ }
+ &.log-message-container-collapsible {
+ .log-message-content {
+ padding-left: 0;
+ &:before {
+ content: "...";
+ float: right;
+ margin-left: 1em;
+ }
+ }
+
+ }
+
+ button, button:active {
+ background: none transparent;
+ border: none transparent;
+ color: @base-font-color;
+ cursor: pointer;
+ float: left;
+ height: 1em;
+ outline: none;
+ padding: 0 .15em;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts
new file mode 100644
index 0000000..edc2515
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts
@@ -0,0 +1,64 @@
+/**
+ * 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 {LogMessageComponent} from './log-message.component';
+
+describe('LogMessageComponent', () => {
+ let component: LogMessageComponent;
+ let fixture: ComponentFixture<LogMessageComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ LogMessageComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LogMessageComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('event handler should call the toggleOpen method', () => {
+ let mockEvent: MouseEvent = document.createEvent('MouseEvent');
+ mockEvent.initEvent('click', true, true);
+ spyOn(component,'toggleOpen');
+ component.onCaretClick(mockEvent);
+ expect(component.toggleOpen).toHaveBeenCalled();
+ });
+
+ it('event handler should prevent the default behaviour of the action', () => {
+ let mockEvent: MouseEvent = document.createEvent('MouseEvent');
+ mockEvent.initEvent('click', true, true);
+ spyOn(mockEvent,'preventDefault');
+ component.onCaretClick(mockEvent);
+ expect(mockEvent.preventDefault).toHaveBeenCalled();
+ });
+
+ it('calling the toggleOpen method should negate the isOpen property', () => {
+ let currentState = component.isOpen;
+ component.toggleOpen();
+ expect(component.isOpen).toEqual(!currentState);
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts
new file mode 100644
index 0000000..b8be61b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts
@@ -0,0 +1,129 @@
+/**
+ * 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, AfterViewInit, ElementRef, ViewChild, OnChanges, SimpleChanges, HostListener, ChangeDetectorRef} from '@angular/core';
+
+/**
+ * This is a simple UI component to display the log message. The goal is to be able to show one line and be collapsile
+ * to show the full log message with new lines.
+ * @class LogMessageComponent
+ */
+@Component({
+ selector: 'log-message',
+ templateUrl: './log-message.component.html',
+ styleUrls: ['./log-message.component.less']
+})
+export class LogMessageComponent implements AfterViewInit, OnChanges {
+
+ /**
+ * This is the element reference to the message log container element. So that we can calculate if the caret should be
+ * displayed or not.
+ * @type ElementRef
+ */
+ @ViewChild('content') content: ElementRef;
+
+ /**
+ * This is the flag property to indicate if the content container is open or not.
+ * @type {boolean}
+ */
+ @Input()
+ isOpen: boolean = false;
+
+ /**
+ * This is a helper property to handle the changes on the parent component. The goal of this input is to be able to
+ * react when the parent component (currently the log-list component) has changed (its size) in a way that the
+ * LogMessageComponent should check if the caret should be visible or not.
+ */
+ @Input()
+ listenChangesOn: any;
+
+ /**
+ * This is a private flag to check if it should display the caret or not, it depends on the size of the size of
+ * the content container element. Handled by the @checkAddCaret method
+ * @type {boolean}
+ */
+ private addCaret: boolean = false;
+
+ /**
+ * This is a primary check if the message content does contain new line (/n) characters. If so than we display the
+ * caret to give a possibility to the user to see the message as it is (pre-wrapped).
+ * @type {boolean}
+ */
+ private isMultiLineMessage: boolean = false;
+
+ constructor(private cdRef:ChangeDetectorRef) {}
+
+ /**
+ * This change handler's goal is to check if we should add the caret or not. Mainly it is because currently we have
+ * the LogListComponent where columns can be added or removed and we have to recheck the visibility of the caret every
+ * changes of the displayed columns.
+ * @param {SimpleChanges} changes
+ */
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.listenChangesOn !== undefined) {
+ this.checkAddCaret();
+ }
+ }
+
+ /**
+ * The goal is to perform a initial caret display check when the component has been initialized.
+ */
+ ngAfterViewInit(): void {
+ let text = this.content.nativeElement.textContent;
+ let newLinePos = text.indexOf('\n');
+ this.isMultiLineMessage = ((text.length - 1) > newLinePos) && (newLinePos > 0);
+ this.checkAddCaret();
+ }
+
+ /**
+ * Since the size of the column is depends on the window size we have to listen the resize event and show/hide the
+ * caret corresponding the new size of the content container element.
+ * Using the arrow function will keep the instance scope.
+ */
+ @HostListener('window:resize', ['$event'])
+ onWindowResize = (): void => {
+ this.isMultiLineMessage || this.checkAddCaret();
+ };
+
+ /**
+ * The goal is to perform a height check on the content container element. It is based on the comparison of the
+ * scrollHeight and the clientHeight.
+ */
+ checkAddCaret = (): void => {
+ let el = this.content.nativeElement;
+ this.addCaret = this.isMultiLineMessage || (el.scrollHeight > el.clientHeight);
+ this.cdRef.detectChanges();
+ };
+
+ /**
+ * This is the click event handler of the caret button element. It will only toggle the isOpen property so that the
+ * component element css classes will follow its state.
+ * @param ev {MouseEvent}
+ */
+ onCaretClick(ev:MouseEvent) {
+ ev.preventDefault();
+ this.toggleOpen();
+ }
+
+ /**
+ * This is a simple property toggle method of the @isOpen property.
+ * The goal is to separate this logic from the event handling and give a way to call it from anywhere.
+ */
+ toggleOpen():void {
+ this.isOpen = !this.isOpen;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/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 1e0f49c..10f4af1 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
@@ -20,46 +20,54 @@
[defaultLabel]="filters.sorting.defaultLabel" [isRightAlign]="true"
class="col-md-12"></filter-dropdown>
</form>
-<div *ngFor="let log of logs; let i = index" class="row">
- <div class="logs-header col-md-12"
- *ngIf="!isServiceLogsFileView && (i === 0 || isDifferentDates(log.logtime, logs[i - 1].logtime))">
- <div class="col-md-12">{{log.logtime | amTz: timeZone | amDateFormat: dateFormat}}</div>
- </div>
- <accordion-panel *ngIf="!isServiceLogsFileView" [toggleId]="'details-' + i" class="col-md-12">
- <ng-template>
- <div *ngIf="isColumnDisplayed('level')" [ngClass]="'hexagon ' + (log.level ? 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 ? 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">
- {{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}}">
- <!-- TODO remove after restyling the table -->
- </div>
- <div class="log-content-inner-wrapper">
- <div class="log-content" *ngIf="isColumnDisplayed('log_message')"
- (contextmenu)="openMessageContextMenu($event)">{{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>
- <log-file-entry *ngIf="isServiceLogsFileView" [time]="log.logtime" [level]="log.level"
- [fileName]="log.file" [lineNumber]="log.line_number" [message]="log.log_message"></log-file-entry>
+<div class="panel panel-default">
+ <div class="panel-body">
+ <table class="table table-hover">
+ <tbody>
+ <ng-container *ngFor="let log of logs; let i = index">
+ <tr *ngIf="!isServiceLogsFileView && (i === 0 || isDifferentDates(log.logtime, logs[i - 1].logtime))"
+ class="log-date-row" >
+ <th attr.colspan="{{displayedColumns.length + 1}}">
+ {{log.logtime | amTz: timeZone | amDateFormat: dateFormat}}
+ </th>
+ </tr>
+ <tr class="log-item-row">
+ <td class="log-action">
+ <dropdown-button iconClass="fa fa-ellipsis-h action" [hideCaret]="true" [options]="logActions"
+ [additionalArgs]="[log]"></dropdown-button>
+ </td>
+ <td *ngIf="isColumnDisplayed('logtime')" class="log-time">
+ <time>
+ {{log.logtime | amTz: timeZone | amDateFormat: timeFormat}}
+ </time>
+ </td>
+ <td *ngIf="isColumnDisplayed('level')" [ngClass]="'log-level ' + log.level.toLowerCase()">
+ <log-level [logEntry]="log"></log-level>
+ </td>
+ <td *ngIf="isColumnDisplayed('type')" [ngClass]="'log-type'">
+ {{log.type}}
+ </td>
+ <td *ngIf="isColumnDisplayed('log_message')" [ngClass]="'log-message'" width="*"
+ (contextmenu)="openMessageContextMenu($event)">
+ <log-message [listenChangesOn]="displayedColumns">{{log.log_message}}</log-message>
+ </td>
+ <ng-container *ngFor="let column of displayedColumns">
+ <td *ngIf="customStyledColumns.indexOf(column.name) === -1"
+ [ngClass]="'log-' + column.name">{{log[column.name]}}</td>
+ </ng-container>
+ </tr>
+ </ng-container>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td attr.colspan="{{displayedColumns.length + 1}}">
+ <pagination class="col-md-12" *ngIf="logs && logs.length" [totalCount]="totalCount"
+ [filtersForm]="filtersForm" [filterInstance]="filters.pageSize" [currentCount]="logs.length"></pagination>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+ <ul #contextmenu data-component="dropdown-list" class="dropdown-menu context-menu" [items]="contextMenuItems"
+ (selectedItemChange)="updateQuery($event)"></ul>
+ </div>
</div>
-<ul #contextmenu *ngIf="!isServiceLogsFileView" data-component="dropdown-list" class="dropdown-menu context-menu"
- [items]="contextMenuItems" (selectedItemChange)="updateQuery($event)"></ul>
-<pagination class="pull-right" *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/38476f7a/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 67d0615..c5c4c5a 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
@@ -17,93 +17,81 @@
@import '../mixins';
-.logs-header {
- // TODO get rid of magic numbers, base on actual design
- margin: 10px 0;
- padding: 5px 0;
- background-color: @list-header-background-color; // TODO implement actual color
- overflow: hidden;
-}
-
-/deep/ filter-dropdown {
- justify-content: flex-end;
-}
-
-.hexagon {
- // TODO remove, since it's not a part of updated design
- left: -7.5px;
-
- &.fatal {
- .common-hexagon(15px, @fatal-color);
- }
-
- &.error {
- .common-hexagon(15px, @error-color);
+:host {
+ /deep/ filter-dropdown {
+ justify-content: flex-end;
}
- &.warn {
- .common-hexagon(15px, @warning-color);
+ .panel-body {
+ overflow: hidden;
+ width: 100%;
}
- &.info {
- .common-hexagon(15px, @info-color);
+ table {
+ width: 100%;
}
- &.debug {
- .common-hexagon(15px, @debug-color);
+ tr.log-date-row, tr.log-date-row:hover {
+ background: @list-header-background-color;
+ border: none transparent;
+ th {
+ border: none transparent;
+ }
}
-
- &.trace {
- .common-hexagon(15px, @trace-color);
+ tr.log-item-row td {
+ background: none transparent;
}
- &.unknown {
- .common-hexagon(15px, @unknown-color);
+ td {
+ &.log-action {
+ min-width: 3em;
+ /deep/ .btn, /deep/ .filter-label {
+ font-size: 1em;
+ height: auto;
+ line-height: 1em;
+ padding: 0;
+ }
+ }
+ &.log-time {
+ color: @grey-color;
+ min-width: 7em;
+ text-align: right;
+ }
+ &.log-level {
+ text-transform: uppercase;
+ min-width: 8em;
+ .log-colors;
+ }
+ &.log-type {
+ color: @link-color;
+ }
+ &.log-message, &.log-path {
+ width: 100%;
+ }
}
-}
-
-.log-status {
- text-transform: uppercase;
- .log-colors;
-}
-
-.log-type {
- color: @link-color;
-}
-
-.log-time {
- color: @grey-color;
-}
-
-.log-content-wrapper {
- position: relative;
- // TODO get rid of magic numbers, base on actual design
- .log-content-inner-wrapper {
- overflow: hidden;
- max-height: @default-line-height * 2em;
- padding-right: 65px;
-
- .log-content {
- white-space: pre-wrap;
+ tr:hover td.log-action {
+ /deep/ .btn {
+ display: inline-block;
}
}
- .log-actions {
- &.collapsing + .log-content-inner-wrapper, &.collapse.in + .log-content-inner-wrapper {
- min-height: 6em;
- max-height: none;
- overflow-x: auto;
+ .table.table-hover>tbody>tr{
+ box-sizing: border-box;
+ border-width: 1px;
+ >td {
+ border-top: 0 none;
}
-
- .action-icon {
- .clickable-item;
- display: block;
- padding: 5px;
+ &:first-of-type {
+ border-top-color: transparent;
+ }
+ &:last-of-type {
+ border-bottom-color: transparent;
}
}
-}
-.context-menu {
- position: fixed;
+ .context-menu {
+ position: fixed;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/38476f7a/ambari-logsearch/ambari-logsearch-web/webpack.config.js
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/webpack.config.js b/ambari-logsearch/ambari-logsearch-web/webpack.config.js
index 7a60df2..75d6aee 100644
--- a/ambari-logsearch/ambari-logsearch-web/webpack.config.js
+++ b/ambari-logsearch/ambari-logsearch-web/webpack.config.js
@@ -81,18 +81,17 @@ module.exports = {
"resolve": {
"extensions": [
".ts",
- ".js"
+ ".js",
+ ".less"
],
"modules": [
- "./node_modules",
- "./node_modules"
+ "node_modules"
],
"symlinks": true
},
"resolveLoader": {
"modules": [
- "./node_modules",
- "./node_modules"
+ "node_modules"
]
},
"entry": {
@@ -229,7 +228,9 @@ module.exports = {
"loader": "less-loader",
"options": {
"sourceMap": false,
- "paths": []
+ "paths": [
+ "./node_modules"
+ ]
}
}
]
@@ -359,7 +360,7 @@ module.exports = {
"loader": "less-loader",
"options": {
"sourceMap": false,
- "paths": []
+ "paths": ["./node_modules"]
}
}
]