Repository: ambari Updated Branches: refs/heads/trunk 5f714cee3 -> 38476f7a1
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/trunk Commit: 38476f7a1a7a371fd0f57af2f931427f1e276d27 Parents: 5f714ce Author: Istvan Tobias <tobias.ist...@gmail.com> Authored: Thu Nov 9 13:24:16 2017 +0200 Committer: ababiichuk <ababiic...@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"] } } ]