AMBARI-22741 Log Search UI: implement graph for access logs. (ababiichuk)
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 da78a71..6258c73 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
@@ -42,6 +42,7 @@
import {AppSettingsService} from '@app/services/storage/app-settings.service';
import {AppStateService} from '@app/services/storage/app-state.service';
import {AuditLogsService} from '@app/services/storage/audit-logs.service';
+import {AuditLogsGraphDataService} from '@app/services/storage/audit-logs-graph-data.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';
@@ -92,6 +93,7 @@
import {HorizontalHistogramComponent} from '@app/components/horizontal-histogram/horizontal-histogram.component';
import {GraphTooltipComponent} from '@app/components/graph-tooltip/graph-tooltip.component';
import {GraphLegendItemComponent} from '@app/components/graph-legend-item/graph-legend-item.component';
+import {TimeLineGraphComponent} from '@app/components/time-line-graph/time-line-graph.component';
import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe';
import {TimerSecondsPipe} from '@app/pipes/timer-seconds.pipe';
@@ -153,6 +155,7 @@
HorizontalHistogramComponent,
GraphTooltipComponent,
GraphLegendItemComponent,
+ TimeLineGraphComponent,
TimeZoneAbbrPipe,
TimerSecondsPipe
],
@@ -182,6 +185,7 @@
AppSettingsService,
AppStateService,
AuditLogsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less
index 0830193..3a57838 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less
@@ -25,6 +25,7 @@
stroke: @base-font-color;
}
.tick {
+ cursor: default;
line {
display: none;
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
index 1bb7f92..167f478 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
@@ -19,7 +19,9 @@
import {AfterViewInit, OnChanges, SimpleChanges, ViewChild, ElementRef, Input} from '@angular/core';
import * as d3 from 'd3';
import * as d3sc from 'd3-scale-chromatic';
-import {GraphPositionOptions, GraphMarginOptions, GraphTooltipInfo, LegendItem} from '@app/classes/graph';
+import {
+ GraphPositionOptions, GraphMarginOptions, GraphTooltipInfo, LegendItem, GraphEventData
+} from '@app/classes/graph';
import {HomogeneousObject} from '@app/classes/object';
import {ServiceInjector} from '@app/classes/service-injector';
import {UtilsService} from '@app/services/utils.service';
@@ -96,6 +98,27 @@
@Input()
reverseYRange: boolean = false;
+ /**
+ * Indicates whether X axis ticks with fractional values should be displayed on chart (if any)
+ * @type {boolean}
+ */
+ @Input()
+ allowFractionalXTicks: boolean = true;
+
+ /**
+ * Indicates whether Y axis ticks with fractional values should be displayed on chart (if any)
+ * @type {boolean}
+ */
+ @Input()
+ allowFractionalYTicks: boolean = true;
+
+ /**
+ * Indicated whether Y values equal to 0 should be skipped in tooltip
+ * @type {boolean}
+ */
+ @Input()
+ skipZeroValuesInTooltip: boolean = true;
+
@ViewChild('graphContainer')
graphContainerRef: ElementRef;
@@ -134,8 +157,8 @@
protected tooltipInfo: GraphTooltipInfo | {} = {};
/**
- * This is the computed position of the tooltip relative to the @graphContainer which is the container of the histogram.
- * It is set when the mousemoving over the bars in the @handleRectMouseMove method.
+ * This is the computed position of the tooltip relative to the @graphContainer which is the container of the graph.
+ * It is set when the mousemoving over the figures in the @handleRectMouseMove method.
*/
private tooltipPosition: GraphPositionOptions;
@@ -230,8 +253,16 @@
protected populate(): void {}
+ /**
+ * Set the domain values for the x scale regarding the given data.
+ * @param formattedData
+ */
protected setXScaleDomain(formattedData?: any): void {}
+ /**
+ * Set the domain for the y scale regarding the given data.
+ * @param formattedData
+ */
protected setYScaleDomain(formattedData?: any): void {}
/**
@@ -271,7 +302,11 @@
* @returns {string|undefined}
*/
protected xAxisTickFormatter = (tick: any, index: number): string | undefined => {
- return tick.toString();
+ if (this.allowFractionalXTicks) {
+ return tick.toString();
+ } else {
+ return Number.isInteger(tick) ? tick.toFixed(0) : undefined;
+ }
};
/**
@@ -283,45 +318,53 @@
* @returns {string|undefined}
*/
protected yAxisTickFormatter = (tick: any, index: number): string | undefined => {
- return tick.toString();
+ if (this.allowFractionalYTicks) {
+ return tick.toString();
+ } else {
+ return Number.isInteger(tick) ? tick.toFixed(0) : undefined;
+ }
};
/**
- * The goal is to handle the mouse over event on the rect svg elements so that we can populate the tooltip info object
+ * The goal is to handle the mouse over event on the svg elements so that we can populate the tooltip info object
* and set the initial position of the tooltip. So we call the corresponding methods.
- * @param d The data for the currently "selected" bar
+ * @param {GraphEventData} d The data for the currently "selected" figure
* @param {number} index The index of the current element in the selection
* @param elements The selection of the elements
*/
- protected handleRectMouseOver = (d: {data: any, [key: string]: any}, index: number, elements: any): void => {
+ protected handleMouseOver = (d: GraphEventData, index: number, elements: HTMLElement[]): void => {
this.setTooltipDataFromChartData(d);
this.setTooltipPosition();
};
/**
- * The goal is to handle the movement of the mouse over the rect svg elements, so that we can set the position of
+ * The goal is to handle the movement of the mouse over the svg elements, so that we can set the position of
* the tooltip by calling the @setTooltipPosition method.
*/
- protected handleRectMouseMove = (): void => {
+ protected handleMouseMove = (): void => {
this.setTooltipPosition();
};
/**
* The goal is to reset the tooltipInfo object so that the tooltip will be hidden.
*/
- protected handleRectMouseOut = (): void => {
+ protected handleMouseOut = (): void => {
this.tooltipInfo = {};
};
/**
* The goal is set the tooltip
- * @param d
+ * @param {GraphEventData} d
*/
- protected setTooltipDataFromChartData(d: {data: any, [key: string]: any}): void {
- let {tick, ...data} = d.data;
- let levelColors = this.colors;
+ protected setTooltipDataFromChartData(d: GraphEventData): void {
+ const {tick, ...data} = d.data,
+ levelColors = this.colors;
+ let tooltipKeys = Object.keys(levelColors);
+ if (this.skipZeroValuesInTooltip) {
+ tooltipKeys = tooltipKeys.filter((key: string): boolean => data[key] > 0)
+ }
this.tooltipInfo = {
- data: Object.keys(levelColors).filter((key: string): boolean => data[key] > 0).map((key: string): object => Object.assign({}, {
+ data: tooltipKeys.map((key: string): object => Object.assign({}, {
color: this.colors[key],
label: this.labels[key],
value: data[key]
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
new file mode 100644
index 0000000..d616b58
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
@@ -0,0 +1,48 @@
+/**
+ * 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 '../../../components/mixins';
+
+:host {
+ background: #ECECEC; // TODO add style according to actual design
+
+ /deep/ svg {
+ cursor: crosshair;
+ }
+
+ .time-gap {
+ color: @base-font-color;
+ font-size: 1.2rem;
+ text-align: center;
+ }
+
+ footer {
+ .default-flex;
+ font-size: 1.2rem;
+ color: @base-font-color;
+ padding: 0 1em .5em;
+ }
+
+ /deep/ rect.drag-area {
+ fill: #fff;
+ }
+
+ /deep/ rect.unselected-drag-area {
+ fill: darken(@main-background-color, 10%);
+ opacity: .6;
+ }
+}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.ts
new file mode 100644
index 0000000..4e0aa10
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.ts
@@ -0,0 +1,250 @@
+/**
+ * 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 {OnInit, Input, Output, EventEmitter} from '@angular/core';
+import * as d3 from 'd3';
+import * as moment from 'moment-timezone';
+import {AppSettingsService} from '@app/services/storage/app-settings.service';
+import {ChartTimeGap, GraphScaleItem} from '@app/classes/graph';
+import {ServiceInjector} from '@app/classes/service-injector';
+import {GraphComponent} from '@app/classes/components/graph/graph.component';
+
+export class TimeGraphComponent extends GraphComponent implements OnInit {
+
+ constructor() {
+ super();
+ this.appSettings = ServiceInjector.injector.get(AppSettingsService);
+ }
+
+ ngOnInit() {
+ this.appSettings.getParameter('timeZone').subscribe((value: string): void => {
+ this.timeZone = value;
+ this.createGraph();
+ });
+ }
+
+ @Input()
+ tickTimeFormat: string = 'MM/DD HH:mm';
+
+ @Input()
+ historyStartEndTimeFormat: string = 'dddd, MMMM DD, YYYY';
+
+ @Output()
+ selectArea: EventEmitter<number[]> = new EventEmitter();
+
+ readonly isTimeGraph: boolean = true;
+
+ readonly allowFractionalXTicks: boolean = false;
+
+ protected appSettings: AppSettingsService;
+
+ protected dragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
+
+ protected dragStartX: number;
+
+ protected minDragX: number;
+
+ protected maxDragX: number;
+
+ protected timeZone: string;
+
+ /**
+ * This property holds the data structure describing the gaps between the xAxis ticks.
+ * The unit property can be: second, minute, hour, day
+ * The value is the number of the given unit.
+ */
+ protected chartTimeGap: ChartTimeGap | null;
+ /**
+ * This is the rectangle element to represent the unselected time range on the left side of the selected time range
+ */
+ protected leftDragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
+ /**
+ * This is the rectangle element to represent the unselected time range on the right side of the selected time range
+ */
+ protected rightDragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
+
+ /**
+ * This is a Date object holding the value of the first tick of the xAxis. It is a helper getter for the template.
+ */
+ protected get firstDateTick(): Date | undefined {
+ const ticks = this.xScale && this.xScale.ticks();
+ return (ticks && ticks.length && ticks[0]) || undefined;
+ }
+
+ /**
+ * This is a Date object holding the value of the last tick of the xAxis. It is a helper getter for the template.
+ */
+ protected get lastDateTick(): Date | undefined {
+ const ticks = this.xScale && this.xScale.ticks();
+ return (ticks && ticks.length && ticks[ticks.length - 1]) || undefined;
+ }
+
+ protected xAxisTickFormatter = (tick: Date): string => {
+ return moment(tick).tz(this.timeZone).format(this.tickTimeFormat);
+ };
+
+ protected setXScaleDomain(data: GraphScaleItem[]): void {
+ this.xScale.domain(d3.extent(data, item => item.tick)).nice();
+ }
+
+ /**
+ * The goal is to calculate the time gap between the given dates. It will return an object representing the unit and
+ * the value in the given unit. Eg.: {unit: 'minute', value: 5}
+ * @param {Date} startDate
+ * @param {Date} endDate
+ * @returns {ChartTimeGap}
+ */
+ protected getTimeGap(startDate: Date, endDate: Date): ChartTimeGap {
+ const startDateMoment = moment(startDate);
+ const endDateMoment = moment(endDate);
+ const diffInWeek: number = endDateMoment.diff(startDateMoment, 'weeks');
+ const diffInDay: number = endDateMoment.diff(startDateMoment, 'days');
+ const diffInHour: number = endDateMoment.diff(startDateMoment, 'hours');
+ const diffInMin: number = endDateMoment.diff(startDateMoment, 'minutes');
+ const diffInSec: number = endDateMoment.diff(startDateMoment, 'seconds');
+ const value = diffInWeek >= 1 ? diffInWeek : (
+ diffInDay >= 1 ? diffInDay : (
+ diffInHour >= 1 ? diffInHour : (diffInMin >= 1 ? diffInMin : diffInSec)
+ )
+ );
+ const unit: string = diffInWeek >= 1 ? 'week' : (
+ diffInDay >= 1 ? `day` : (
+ diffInHour >= 1 ? `hour` : (diffInMin >= 1 ? `minute` : `second`)
+ )
+ );
+ const label = `histogram.gap.${unit}${value > 1 ? 's' : ''}`;
+ return {
+ value,
+ unit,
+ label
+ };
+ }
+
+ /**
+ * The goal is to have a simple function to set the time gap corresponding to the xScale ticks.
+ * It will reset the time gap if the xScale is not set or there are no ticks.
+ */
+ protected setChartTimeGapByXScale(): void {
+ let ticks = this.xScale && this.xScale.ticks();
+ if (ticks && ticks.length) {
+ this.setChartTimeGap(ticks[0], ticks[1] || ticks[0]);
+ } else {
+ this.resetChartTimeGap();
+ }
+ }
+
+ /**
+ * Simply reset the time gap property to null.
+ */
+ protected resetChartTimeGap(): void {
+ this.chartTimeGap = null;
+ }
+
+ /**
+ * The goal is to have a single point where we set the chartTimeGap property corresponding the given timerange.
+ * @param {Date} startDate
+ * @param {Date} endDate
+ */
+ protected setChartTimeGap(startDate: Date, endDate: Date): void {
+ this.chartTimeGap = this.getTimeGap(startDate, endDate);
+ }
+
+ protected getTimeRangeByXRanges(startX: number, endX: number): [number, number] {
+ const xScaleInterval = this.xScale.domain().map((point: Date): number => point.valueOf());
+ const xScaleLength = xScaleInterval[1] - xScaleInterval[0];
+ const ratio = xScaleLength / this.width;
+ return [Math.round(xScaleInterval[0] + ratio * startX), Math.round(xScaleInterval[0] + ratio * endX)];
+ }
+
+ /**
+ * The goal is to create the two shadow rectangle beside the selected area. Actually we blurout the not selected
+ * timeranges
+ * @param {number} startX This is the starting position of the drag event withing the container
+ * @param {number} currentX This is the ending point of the drag within the container
+ */
+ protected createInvertDragArea(startX: number, currentX: number): void {
+ const height: number = this.height + this.margin.top + this.margin.bottom;
+ this.leftDragArea = this.svg.insert('rect').attr('height', height).attr('class', 'unselected-drag-area');
+ this.rightDragArea = this.svg.insert('rect').attr('height', height).attr('class', 'unselected-drag-area');
+ this.setInvertDragArea(startX, currentX);
+ }
+
+ /**
+ * Set the position and the width of the blur/shadow rectangles of the unselected area(s).
+ * @param {number} startX The start point of the selected area.
+ * @param {number} currentX The end point of the selected area.
+ */
+ protected setInvertDragArea(startX: number, currentX: number): void {
+ const left: number = Math.min(startX, currentX);
+ const right: number = Math.max(startX, currentX);
+ const rightAreaWidth: number = Math.max(0, this.width - right);
+ const leftAreaWidth: number = Math.max(0, left);
+ this.leftDragArea.attr('x', 0).attr('width', leftAreaWidth);
+ this.rightDragArea.attr('x', right).attr('width', rightAreaWidth);
+ }
+
+ /**
+ * The goal is to have a single point where we remove the rectangles of the blur/shadow, unselected time range(s)
+ */
+ protected clearInvertDragArea(): void {
+ this.leftDragArea.remove();
+ this.rightDragArea.remove();
+ }
+
+ protected setDragBehavior(): void {
+ this.minDragX = this.margin.left;
+ this.maxDragX = this.graphContainer.clientWidth;
+ d3.selectAll(`svg#${this.svgId}`).call(d3.drag()
+ .on('start', (datum: undefined, index: number, containers: d3.ContainerElement[]): void => {
+ if (this.dragArea) {
+ this.dragArea.remove();
+ }
+ this.dragStartX = Math.max(0, this.getDragX(containers[0]) - this.margin.left);
+ this.dragArea = this.svg.insert('rect', ':first-child').attr('x', this.dragStartX).attr('y', 0).attr('width', 0)
+ .attr('height', this.height).attr('class', 'drag-area');
+ })
+ .on('drag', (datum: undefined, index: number, containers: d3.ContainerElement[]): void => {
+ const mousePos = this.getDragX(containers[0]);
+ const currentX = Math.max(mousePos, this.minDragX) - this.margin.left;
+ const startX = Math.min(currentX, this.dragStartX);
+ const currentWidth = Math.abs(currentX - this.dragStartX);
+ this.dragArea.attr('x', startX).attr('width', currentWidth);
+ let timeRange = this.getTimeRangeByXRanges(startX, startX + currentWidth);
+ this.setChartTimeGap(new Date(timeRange[0]), new Date(timeRange[1]));
+ })
+ .on('end', (): void => {
+ const dragAreaDetails = this.dragArea.node().getBBox();
+ const startX = Math.max(0, dragAreaDetails.x);
+ const endX = Math.min(this.width, dragAreaDetails.x + dragAreaDetails.width);
+ const dateRange: [number, number] = this.getTimeRangeByXRanges(startX, endX);
+ this.selectArea.emit(dateRange);
+ this.dragArea.remove();
+ this.setChartTimeGap(new Date(dateRange[0]), new Date(dateRange[1]));
+ })
+ );
+ d3.selectAll(`svg#${this.svgId} .value, svg#${this.svgId} .axis`).call(d3.drag().on('start', (): void => {
+ d3.event.sourceEvent.stopPropagation();
+ }));
+ }
+
+ protected getDragX(element: d3.ContainerElement): number {
+ return d3.mouse(element)[0];
+ }
+
+
+}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts
index 3992828..cc07ec8 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts
@@ -40,3 +40,22 @@
tick: number;
[key: string]: number;
}
+
+export interface ChartTimeGap {
+ value: number;
+ unit: string;
+ label: string;
+}
+
+export interface GraphEventData extends Array<number> {
+ data: GraphScaleItem;
+}
+
+export type GraphLinePoint = GraphScaleItem & {
+ color: string;
+}
+
+export interface GraphLineData {
+ points: GraphScaleItem[];
+ key: string;
+}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts
index f996d92..f2d0696 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/store.ts
@@ -46,6 +46,7 @@
appSettings: AppSettings;
appState: AppState;
auditLogs: AuditLog[];
+ auditLogsGraphData: BarGraph[];
serviceLogs: ServiceLog[];
serviceLogsHistogramData: BarGraph[];
serviceLogsTruncated: ServiceLog[];
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-histogram-query-params.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-graph-query-params.spec.ts
similarity index 95%
rename from ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-histogram-query-params.spec.ts
rename to ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-graph-query-params.spec.ts
index eb891ed..8951dff 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-histogram-query-params.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-graph-query-params.spec.ts
@@ -16,9 +16,9 @@
* limitations under the License.
*/
-import {ServiceLogsHistogramQueryParams} from './service-logs-histogram-query-params';
+import {AuditLogsGraphQueryParams} from './audit-logs-graph-query-params';
-describe('ServiceLogsHistogramQueryParams', () => {
+describe('AuditLogsGraphQueryParams', () => {
describe('constructor', () => {
const cases = [
@@ -194,7 +194,7 @@
cases.forEach(test => {
it(test.title, () => {
- const paramsObject = new ServiceLogsHistogramQueryParams(test.options);
+ const paramsObject = new AuditLogsGraphQueryParams(test.options);
expect(paramsObject.unit).toEqual(test.unit);
});
});
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-graph-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-graph-query-params.ts
new file mode 100644
index 0000000..7e63acf
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-graph-query-params.ts
@@ -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 {QueryParams} from '@app/classes/queries/query-params';
+
+export class AuditLogsGraphQueryParams extends QueryParams {
+ constructor(options: AuditLogsGraphQueryParams) {
+ let unit;
+ const diffTimeStamp = new Date(options.to).valueOf() - new Date(options.from).valueOf();
+ switch (true) {
+ case diffTimeStamp <= 1000:
+ unit = '+100MILLISECOND';
+ break;
+ case diffTimeStamp <= 30000:
+ unit = '+500MILLISECOND';
+ break;
+ case diffTimeStamp <= 60000:
+ unit = '+2SECOND';
+ break;
+ case diffTimeStamp < 1800000:
+ unit = '+1MINUTE';
+ break;
+ case diffTimeStamp < 7200000:
+ unit = '+2MINUTE';
+ break;
+ case diffTimeStamp < 21600000:
+ unit = '+5MINUTE';
+ break;
+ case diffTimeStamp <= 36000000:
+ unit = '+10MINUTE';
+ break;
+ case diffTimeStamp <= 86400000:
+ unit = '+1HOUR';
+ break;
+ case diffTimeStamp < 1296000000:
+ unit = '+8HOUR';
+ break;
+ case diffTimeStamp <= 7776000000:
+ unit = '+1DAY';
+ break;
+ default:
+ unit = '+1MONTH';
+ break;
+ }
+ options.unit = unit;
+ super(options);
+ }
+
+ from: string;
+ to: string;
+ unit?: string;
+ includeQuery?: string;
+ excludeQuery?: string;
+}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-histogram-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-histogram-query-params.ts
index 148aa62..ff83a8b 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-histogram-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-histogram-query-params.ts
@@ -16,55 +16,9 @@
* limitations under the License.
*/
-import {QueryParams} from '@app/classes/queries/query-params';
+import {AuditLogsGraphQueryParams} from '@app/classes/queries/audit-logs-graph-query-params';
-export class ServiceLogsHistogramQueryParams extends QueryParams {
- constructor(options: ServiceLogsHistogramQueryParams) {
- let unit;
- const diffTimeStamp = new Date(options.to).valueOf() - new Date(options.from).valueOf();
- switch (true) {
- case diffTimeStamp <= 1000:
- unit = '+100MILLISECOND';
- break;
- case diffTimeStamp <= 30000:
- unit = '+500MILLISECOND';
- break;
- case diffTimeStamp <= 60000:
- unit = '+2SECOND';
- break;
- case diffTimeStamp < 1800000:
- unit = '+1MINUTE';
- break;
- case diffTimeStamp < 7200000:
- unit = '+2MINUTE';
- break;
- case diffTimeStamp < 21600000:
- unit = '+5MINUTE';
- break;
- case diffTimeStamp <= 36000000:
- unit = '+10MINUTE';
- break;
- case diffTimeStamp <= 86400000:
- unit = '+1HOUR';
- break;
- case diffTimeStamp < 1296000000:
- unit = '+8HOUR';
- break;
- case diffTimeStamp <= 7776000000:
- unit = '+1DAY';
- break;
- default:
- unit = '+1MONTH';
- break;
- }
- options.unit = unit;
- super(options);
- }
- from: string;
- to: string;
- unit?: string;
+export class ServiceLogsHistogramQueryParams extends AuditLogsGraphQueryParams {
clusters?: string;
level?: string;
- includeQuery?: string;
- excludeQuery?: string;
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
index 3c5852a..fe9f697 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
@@ -20,11 +20,13 @@
<audit-logs-table *ngSwitchCase="'logs'" [totalCount]="totalCount" [logs]="logs" [columns]="columns"
[filtersForm]="filtersForm"></audit-logs-table>
<div *ngSwitchCase="'summary'" class="row">
- <collapsible-panel title="{{'logs.topUsers' | translate: usersGraphTitleParams}}" class="col-md-6">
- <horizontal-histogram [data]="topUsersGraphData"></horizontal-histogram>
+ <collapsible-panel commonTitle="{{'logs.topUsers' | translate: usersGraphTitleParams}}" class="col-md-6">
+ <horizontal-histogram [data]="topUsersGraphData" [allowFractionalXTicks]="false"
+ svgId="top-users-graph"></horizontal-histogram>
</collapsible-panel>
- <collapsible-panel title="{{'logs.topResources' | translate: resourcesGraphTitleParams}}" class="col-md-6">
- <horizontal-histogram [data]="topResourcesGraphData"></horizontal-histogram>
+ <collapsible-panel commonTitle="{{'logs.topResources' | translate: resourcesGraphTitleParams}}" class="col-md-6">
+ <horizontal-histogram [data]="topResourcesGraphData" [allowFractionalXTicks]="false"
+ svgId="top-resources-graph"></horizontal-histogram>
</collapsible-panel>
</div>
</ng-container>
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
index 260b383..f7d0cdc 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
@@ -22,6 +22,7 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
import {
ServiceLogsHistogramDataService, serviceLogsHistogramData
@@ -63,6 +64,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
appSettings,
@@ -85,6 +87,7 @@
AuditLogsFieldsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
+ AuditLogsGraphDataService,
AppSettingsService,
AppStateService,
ClustersService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.spec.ts
index b6206db..1a2f4de 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.spec.ts
@@ -26,8 +26,11 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.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 {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {AppStateService, appState} from '@app/services/storage/app-state.service';
import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
@@ -72,6 +75,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
serviceLogsTruncated,
@@ -93,6 +97,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
index 4d0bd39..a671946 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
@@ -15,13 +15,13 @@
limitations under the License.
-->
<div [ngClass]="{'panel': true, 'panel-default': true, 'panel-collapsible': true, 'panel-collapsed': isCollapsed}">
- <div [ngClass]="{'panel-heading': true}" (click)="handleCollapseBtnClick($event)">
+ <div class="panel-heading" (click)="handleCollapseBtnClick($event)">
<a [attr.aria-collapsed]="isCollapsed">
<i [ngClass]="{'fa': true, 'fa-caret-down': !isCollapsed, 'fa-caret-right': isCollapsed}"></i>
- {{((isCollapsed ? collapsedTitle : openTitle) || title) | translate}}
+ {{((isCollapsed ? collapsedTitle : openTitle) || commonTitle) | translate}}
</a>
</div>
- <div [ngClass]="{'panel-body': true}" [attr.aria-collapsed]="isCollapsed">
+ <div class="panel-body" [attr.aria-collapsed]="isCollapsed">
<ng-content></ng-content>
</div>
</div>
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.spec.ts
index 5f5f1b0..f92f30a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.spec.ts
@@ -120,7 +120,7 @@
let title = 'Title';
let headingEl = el.querySelector('.panel-heading');
component.openTitle = '';
- component.title = title;
+ component.commonTitle = title;
component.isCollapsed = false;
fixture.detectChanges();
expect(headingEl.textContent).toContain(title);
@@ -130,7 +130,7 @@
let title = 'Title';
let headingEl = el.querySelector('.panel-heading');
component.collapsedTitle = '';
- component.title = title;
+ component.commonTitle = title;
component.isCollapsed = true;
fixture.detectChanges();
expect(headingEl.textContent).toContain(title);
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts
index f82823a..5e86bfa 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts
@@ -39,7 +39,7 @@
* @type {string}
*/
@Input()
- title: string = '';
+ commonTitle: string = '';
/**
* The panel's title for the opened state
@@ -82,7 +82,7 @@
/**
* The goal is to simply negate the current collapse state.
*/
- toggleCollapsed():void {
+ toggleCollapsed(): void {
this.isCollapsed = !this.isCollapsed;
}
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 9f2bb16..b9f9540 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
@@ -28,9 +28,12 @@
import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
-import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
+import {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {UtilsService} from '@app/services/utils.service';
@@ -65,6 +68,7 @@
hosts,
auditLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogs,
serviceLogsFields,
serviceLogsHistogramData,
@@ -81,6 +85,7 @@
HostsService,
AuditLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
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 63824cb..dd602d7 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
@@ -24,8 +24,11 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.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 {
+ 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';
@@ -62,6 +65,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
appSettings,
@@ -85,6 +89,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
AppSettingsService,
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 082082c..4c93cbe 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
@@ -28,9 +28,12 @@
import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
-import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
+import {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {ComponentActionsService} from '@app/services/component-actions.service';
@@ -65,6 +68,7 @@
hosts,
auditLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogs,
serviceLogsFields,
serviceLogsHistogramData,
@@ -81,6 +85,7 @@
HostsService,
AuditLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
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 d085f3e..61d30d1 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
@@ -24,6 +24,7 @@
import {AppStateService, appState} from '@app/services/storage/app-state.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
import {
@@ -79,6 +80,7 @@
appState,
auditLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogs,
serviceLogsFields,
serviceLogsHistogramData,
@@ -95,6 +97,7 @@
AppStateService,
AuditLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
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 c9f9b52..8df1d54 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
@@ -28,8 +28,11 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.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 {
+ 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 {TabsService, tabs} from '@app/services/storage/tabs.service';
@@ -67,6 +70,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
appState,
@@ -83,6 +87,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
AppStateService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts
index 9553e2e..8cc3149 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts
@@ -28,7 +28,7 @@
export class HorizontalHistogramComponent extends GraphComponent {
/**
- * Thickness of horizontal bar o the graph
+ * Thickness of horizontal bar of the graph
* @type {number}
*/
@Input()
@@ -81,9 +81,9 @@
}).attr('height', item => item[0] === item[1] ? '0' : barSize.toString())
.attr('width', item => this.xScale(item[1]) - this.xScale(item[0]))
.style('fill', (item, index) => this.orderedColors[index])
- .on('mouseover', this.handleRectMouseOver)
- .on('mousemove', this.handleRectMouseMove)
- .on('mouseout', this.handleRectMouseOut);
+ .on('mouseover', this.handleMouseOver)
+ .on('mousemove', this.handleMouseMove)
+ .on('mouseout', this.handleMouseOut);
}
protected setXScaleDomain(): void {
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
index 7bd87ad..c346c9a 100644
--- 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
@@ -21,8 +21,11 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.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 {
+ 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';
@@ -60,6 +63,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
appSettings,
@@ -76,6 +80,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
AppSettingsService,
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 8e507ae..412fd2c 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
@@ -34,21 +34,31 @@
</div>
<!-- TODO use plugin for singular/plural -->
- <div class="logs-header col-md-12">{{
+ <div *ngIf="logsType === 'serviceLogs'" class="logs-header col-md-12">{{
(!totalEventsFoundMessageParams.totalCount ? 'logs.noEventFound' :
(totalEventsFoundMessageParams.totalCount === 1 ? 'logs.oneEventFound' : 'logs.totalEventFound'))
| translate: totalEventsFoundMessageParams
}}</div>
</div>
- <collapsible-panel openTitle="logs.hideGraph" collapsedTitle="logs.showGraph">
- <time-histogram [data]="histogramData" [colors]="serviceLogsHistogramColors" svgId="service-logs-histogram"
- (selectArea)="setCustomTimeRange($event[0], $event[1])"></time-histogram>
- </collapsible-panel>
<ng-container [ngSwitch]="logsType">
- <service-logs-table *ngSwitchCase="'serviceLogs'" [totalCount]="totalCount" [logs]="serviceLogs | async"
- [columns]="serviceLogsColumns | async" [filtersForm]="filtersForm"></service-logs-table>
- <audit-logs-entries *ngSwitchCase="'auditLogs'" [totalCount]="totalCount" [logs]="auditLogs | async"
- [columns]="auditLogsColumns | async" [filtersForm]="filtersForm"></audit-logs-entries>
+ <ng-container *ngSwitchCase="'serviceLogs'">
+ <collapsible-panel openTitle="logs.hideGraph" collapsedTitle="logs.showGraph">
+ <time-histogram (selectArea)="setCustomTimeRange($event[0], $event[1])" [data]="serviceLogsHistogramData"
+ [colors]="serviceLogsHistogramColors" [allowFractionalYTicks]="false"
+ svgId="service-logs-histogram"></time-histogram>
+ </collapsible-panel>
+ <service-logs-table [totalCount]="totalCount" [logs]="serviceLogs | async" [columns]="serviceLogsColumns | async"
+ [filtersForm]="filtersForm"></service-logs-table>
+ </ng-container>
+ <ng-container *ngSwitchCase="'auditLogs'">
+ <collapsible-panel commonTitle="logs.duration">
+ <time-line-graph (selectArea)="setCustomTimeRange($event[0], $event[1])" [data]="auditLogsGraphData"
+ [allowFractionalYTicks]="false" [skipZeroValuesInTooltip]="false"
+ svgId="audit-logs-graph"></time-line-graph>
+ </collapsible-panel>
+ <audit-logs-entries [totalCount]="totalCount" [logs]="auditLogs | async" [columns]="auditLogsColumns | async"
+ [filtersForm]="filtersForm"></audit-logs-entries>
+ </ng-container>
</ng-container>
<log-context *ngIf="isServiceLogContextView" [id]="activeLog.id" [hostName]="activeLog.host_name"
[componentName]="activeLog.component_name"></log-context>
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 2bb8731..8a3d985 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
@@ -26,9 +26,12 @@
import {ComponentsService, components} from '@app/services/storage/components.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
-import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
+import {
+ 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 {TabsService, tabs} from '@app/services/storage/tabs.service';
@@ -65,6 +68,7 @@
components,
auditLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogs,
serviceLogsFields,
serviceLogsHistogramData,
@@ -85,6 +89,7 @@
ComponentsService,
AuditLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
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 6d50a17..d853bb5 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
@@ -16,11 +16,12 @@
* limitations under the License.
*/
-import {Component, ElementRef, ViewChild, HostListener} from '@angular/core';
+import {Component, OnInit, ElementRef, ViewChild, HostListener} from '@angular/core';
import {FormGroup} from '@angular/forms';
import {Observable} from 'rxjs/Observable';
import {LogsContainerService} from '@app/services/logs-container.service';
import {ServiceLogsHistogramDataService} from '@app/services/storage/service-logs-histogram-data.service';
+import {AuditLogsGraphDataService} from '@app/services/storage/audit-logs-graph-data.service';
import {AppStateService} from '@app/services/storage/app-state.service';
import {TabsService} from '@app/services/storage/tabs.service';
import {AuditLog} from '@app/classes/models/audit-log';
@@ -38,18 +39,27 @@
templateUrl: './logs-container.component.html',
styleUrls: ['./logs-container.component.less']
})
-export class LogsContainerComponent {
+export class LogsContainerComponent implements OnInit {
constructor(
- private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: AppStateService,
- private tabsStorage: TabsService, private logsContainer: LogsContainerService
+ private appState: AppStateService, private tabsStorage: TabsService, private logsContainer: LogsContainerService,
+ private serviceLogsHistogramStorage: ServiceLogsHistogramDataService,
+ private auditLogsGraphStorage: AuditLogsGraphDataService
) {
+ }
+
+ ngOnInit() {
this.logsContainer.loadColumnsNames();
- appState.getParameter('activeLogsType').subscribe((value: LogsType) => this.logsType = value);
- serviceLogsHistogramStorage.getAll().subscribe((data: BarGraph[]): void => {
- this.histogramData = this.logsContainer.getHistogramData(data);
+ this.appState.getParameter('activeLogsType').subscribe((value: LogsType) => this.logsType = value);
+ this.serviceLogsHistogramStorage.getAll().subscribe((data: BarGraph[]): void => {
+ this.serviceLogsHistogramData = this.logsContainer.getGraphData(data, Object.keys(this.logsContainer.colors));
});
- appState.getParameter('isServiceLogContextView').subscribe((value: boolean) => this.isServiceLogContextView = value);
+ this.auditLogsGraphStorage.getAll().subscribe((data: BarGraph[]): void => {
+ this.auditLogsGraphData = this.logsContainer.getGraphData(data);
+ });
+ this.appState.getParameter('isServiceLogContextView').subscribe((value: boolean): void => {
+ this.isServiceLogContextView = value;
+ });
}
@ViewChild('container') containerRef: ElementRef;
@@ -74,7 +84,9 @@
return this.logsContainer.totalCount;
}
- histogramData: HomogeneousObject<HomogeneousObject<number>>;
+ serviceLogsHistogramData: HomogeneousObject<HomogeneousObject<number>>;
+
+ auditLogsGraphData: HomogeneousObject<HomogeneousObject<number>>;
get serviceLogsHistogramColors(): HomogeneousObject<string> {
return this.logsContainer.colors;
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 67d9423..3b210a3 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
@@ -28,9 +28,12 @@
import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
-import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
+import {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {ComponentActionsService} from '@app/services/component-actions.service';
@@ -64,6 +67,7 @@
hosts,
auditLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogs,
serviceLogsFields,
serviceLogsHistogramData,
@@ -80,6 +84,7 @@
HostsService,
AuditLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts
index 0c323f7..05420ff 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts
@@ -26,8 +26,11 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.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 {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {AppStateService, appState} from '@app/services/storage/app-state.service';
import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
@@ -75,6 +78,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
serviceLogsTruncated,
@@ -96,6 +100,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
index 720f55e..be68d08 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
@@ -27,7 +27,7 @@
</div>
</header>
<div #graphContainer></div>
-<footer *ngIf="isTimeGraph && (firstDateTick || lastDateTick)">
+<footer *ngIf="firstDateTick || lastDateTick">
<div *ngIf="firstDateTick">{{firstDateTick | amTz: timeZone | amDateFormat: historyStartEndTimeFormat}}</div>
<div *ngIf="lastDateTick">{{lastDateTick | amTz: timeZone | amDateFormat: historyStartEndTimeFormat}}</div>
</footer>
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
index 364517c..e085d90 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
@@ -19,43 +19,7 @@
@import '../mixins';
:host {
- position: relative;
- background: #ECECEC; // TODO add style according to actual design
-
- /deep/ .axis {
- .tick {
- cursor: default;
- }
- }
-
- /deep/ svg {
- cursor: crosshair;
- }
-
header {
padding: @graph-padding;
}
-
- .time-gap {
- color: @base-font-color;
- font-size: 1.2rem;
- text-align: center;
- }
-
- footer {
- .default-flex;
- font-size: 1.2rem;
- color: @base-font-color;
- padding: 0 1em .5em;
- }
-
- /deep/ rect.drag-area {
- fill: #fff;
- }
-
- /deep/ rect.unselected-drag-area {
- fill: darken(@main-background-color, 10%);
- opacity: .6;
- }
-
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
index 09cd5d8..b429cdc 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
@@ -107,7 +107,6 @@
],
providers: [
AppSettingsService,
- ServiceLogsHistogramDataService,
LogsContainerService,
{
provide: HttpClientService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
index 8544677..e021075 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
@@ -16,31 +16,25 @@
* limitations under the License.
*/
-import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
+import {Component, Input} from '@angular/core';
import * as d3 from 'd3';
-import * as moment from 'moment-timezone';
-import {AppSettingsService} from '@app/services/storage/app-settings.service';
-import {GraphComponent} from '@app/classes/components/graph/graph.component';
+import {TimeGraphComponent} from '@app/classes/components/graph/time-graph.component';
import {GraphScaleItem} from '@app/classes/graph';
@Component({
selector: 'time-histogram',
templateUrl: './time-histogram.component.html',
- styleUrls: ['../../classes/components/graph/graph.component.less', './time-histogram.component.less']
+ styleUrls: [
+ '../../classes/components/graph/graph.component.less', '../../classes/components/graph/time-graph.component.less',
+ './time-histogram.component.less'
+ ]
})
-export class TimeHistogramComponent extends GraphComponent implements OnInit {
+export class TimeHistogramComponent extends TimeGraphComponent {
- constructor(private appSettings: AppSettingsService) {
+ constructor() {
super();
}
- ngOnInit() {
- this.appSettings.getParameter('timeZone').subscribe((value: string): void => {
- this.timeZone = value;
- this.createGraph();
- });
- }
-
@Input()
columnWidth = {
second: 40,
@@ -50,153 +44,18 @@
base: 20
};
- @Output()
- selectArea: EventEmitter<number[]> = new EventEmitter();
-
- readonly isTimeGraph: boolean = true;
-
- private timeZone: string;
-
- private dragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
-
- private dragStartX: number;
-
- private minDragX: number;
-
- private maxDragX: number;
-
- private readonly tickTimeFormat: string = 'MM/DD HH:mm';
-
- private readonly historyStartEndTimeFormat: string = 'dddd, MMMM DD, YYYY';
-
- histogram: any;
-
- /**
- * This property holds the data structure describing the gaps between the xAxis ticks.
- * The unit property can be: second, minute, hour, day
- * The value is the number of the given unit.
- */
- private chartTimeGap: {value: number, unit: string, label: string} | null;
- /**
- * This is the rectangle element to represent the unselected time range on the left side of the selected time range
- */
- private leftDragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
- /**
- * This is the rectangle element to represent the unselected time range on the right side of the selected time range
- */
- private rightDragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
- /**
- * This is a Date object holding the value of the first tick of the xAxis. It is a helper getter for the template.
- */
- private get firstDateTick(): Date | undefined {
- const ticks = this.xScale && this.xScale.ticks();
- return (ticks && ticks.length && ticks[0]) || undefined;
- }
- /**
- * This is a Date object holding the value of the last tick of the xAxis. It is a helper getter for the template.
- */
- private get lastDateTick(): Date | undefined {
- const ticks = this.xScale && this.xScale.ticks();
- return (ticks && ticks.length && ticks[ticks.length-1]) || undefined;
- }
-
- protected xAxisTickFormatter = (tick: Date): string => {
- return moment(tick).tz(this.timeZone).format(this.tickTimeFormat);
- };
-
- protected yAxisTickFormatter = (tick: number): string | undefined => {
- return Number.isInteger(tick) ? tick.toFixed(0) : undefined;
- };
-
- /**
- * The goal is to calculate the time gap between the given dates. It will return an object representing the unit and
- * the value in the given unit. Eg.: {unit: 'minute', value: 5}
- * @param {Date} startDate
- * @param {Date} endDate
- * @returns {{value: number; unit: string, label: string}}
- */
- private getTimeGap(startDate: Date, endDate: Date): {value: number, unit: string, label: string} {
- const startDateMoment = moment(startDate);
- const endDateMoment = moment(endDate);
- const diffInWeek: number = endDateMoment.diff(startDateMoment, 'weeks');
- const diffInDay: number = endDateMoment.diff(startDateMoment, 'days');
- const diffInHour: number = endDateMoment.diff(startDateMoment, 'hours');
- const diffInMin: number = endDateMoment.diff(startDateMoment, 'minutes');
- const diffInSec: number = endDateMoment.diff(startDateMoment, 'seconds');
- const value = diffInWeek >= 1 ? diffInWeek : (
- diffInDay >= 1 ? diffInDay : (
- diffInHour >= 1 ? diffInHour : (diffInMin >= 1 ? diffInMin : diffInSec)
- )
- );
- const unit: string = diffInWeek >= 1 ? 'week' : (
- diffInDay >= 1 ? `day` : (
- diffInHour >= 1 ? `hour` : (diffInMin >= 1 ? `minute` : `second`)
- )
- );
- const label = `histogram.gap.${unit}${value>1 ? 's' : ''}`;
- return {
- value,
- unit,
- label
- };
- }
-
- /**
- * The goal is to have a simple function to set the time gap corresponding to the xScale ticks.
- * It will reset the time gap if the xScale is not set or there are no ticks.
- */
- private setChartTimeGapByXScale() {
- let ticks = this.xScale && this.xScale.ticks();
- if (ticks && ticks.length) {
- this.setChartTimeGap(ticks[0], ticks[1] || ticks[0]);
- } else {
- this.resetChartTimeGap();
- }
- }
-
- /**
- * Simply reset the time gap property to null.
- */
- private resetChartTimeGap(): void {
- this.chartTimeGap = null;
- }
-
- /**
- * The goal is to have a single point where we set the chartTimeGap property corresponding the given timerange.
- * @param {Date} startDate
- * @param {Date} endDate
- */
- private setChartTimeGap(startDate: Date, endDate: Date): void {
- this.chartTimeGap = this.getTimeGap(startDate, endDate);
- }
-
- /**
- * Set the domain for the y scale regarding the given data. The maximum value of the data is the sum of the log level
- * values.
- * An example data: [{tick: 1233455677, WARN: 12, ERROR: 123}]
- * @param {GraphScaleItem[]} data
- */
protected setYScaleDomain(data: GraphScaleItem[]): void {
const keys = Object.keys(this.labels);
const maxYValue = d3.max(data, item => keys.reduce((sum: number, key: string): number => sum + item[key], 0));
this.yScale.domain([0, maxYValue]);
}
- /**
- * Set the domain values for the x scale regarding the given data.
- * An example data: [{tick: 1233455677, WARN: 12, ERROR: 123}]
- * @param {GraphScaleItem[]} data
- */
- protected setXScaleDomain(data: GraphScaleItem[]): void {
- this.xScale.domain(d3.extent(data, item => item.tick)).nice();
- }
-
protected populate(): void {
const keys = Object.keys(this.colors);
const data = this.data;
const timeStamps = Object.keys(data);
// we create a more consumable data structure for d3
- const formattedData = timeStamps.map((timeStamp: string): {tick: number, [key: string]: number} => Object.assign({
+ const formattedData = timeStamps.map((timeStamp: string): GraphScaleItem => Object.assign({
tick: Number(timeStamp)
}, data[timeStamp]));
const layers = d3.stack().keys(keys)(formattedData);
@@ -227,92 +86,10 @@
.attr('height', item => this.yScale(item[0]) - this.yScale(item[1]))
.attr('width', columnWidth.toString())
.style('fill', (item, index) => this.orderedColors[index])
- .on('mouseover', this.handleRectMouseOver)
- .on('mousemove', this.handleRectMouseMove)
- .on('mouseout', this.handleRectMouseOut);
+ .on('mouseover', this.handleMouseOver)
+ .on('mousemove', this.handleMouseMove)
+ .on('mouseout', this.handleMouseOut);
this.setDragBehavior();
}
- private getTimeRangeByXRanges(startX: number, endX: number): [number, number] {
- const xScaleInterval = this.xScale.domain().map((point: Date): number => point.valueOf());
- const xScaleLength = xScaleInterval[1] - xScaleInterval[0];
- const ratio = xScaleLength / this.width;
- return [Math.round(xScaleInterval[0] + ratio * startX), Math.round(xScaleInterval[0] + ratio * endX)];
- }
-
- /**
- * The goal is to create the two shadow rectangle beside the selected area. Actually we blurout the not selected
- * timeranges
- * @param {number} startX This is the starting position of the drag event withing the container
- * @param {number} currentX This is the ending point of the drag within the container
- */
- private createInvertDragArea(startX: number, currentX: number): void {
- const height: number = this.height + this.margin.top + this.margin.bottom;
- this.leftDragArea = this.svg.insert('rect').attr('height', height).attr('class', 'unselected-drag-area');
- this.rightDragArea = this.svg.insert('rect').attr('height', height).attr('class', 'unselected-drag-area');
- this.setInvertDragArea(startX, currentX);
- }
-
- /**
- * Set the position and the width of the blur/shadow rectangles of the unselected area(s).
- * @param {number} startX The start point of the selected area.
- * @param {number} currentX The end point of the selected area.
- */
- private setInvertDragArea(startX: number, currentX: number): void {
- const left: number = Math.min(startX, currentX);
- const right: number = Math.max(startX, currentX);
- const rightAreaWidth: number = Math.max(0, this.width - right);
- const leftAreaWidth: number = Math.max(0, left);
- this.leftDragArea.attr('x', 0).attr('width', leftAreaWidth);
- this.rightDragArea.attr('x', right).attr('width', rightAreaWidth);
- }
-
- /**
- * The goal is to have a single point where we remove the rectangles of the blur/shadow, unselected time range(s)
- */
- private clearInvertDragArea(): void {
- this.leftDragArea.remove();
- this.rightDragArea.remove();
- }
-
- private setDragBehavior(): void {
- this.minDragX = this.margin.left;
- this.maxDragX = this.graphContainer.clientWidth;
- d3.selectAll(`svg#${this.svgId}`).call(d3.drag()
- .on('start', (datum: undefined, index: number, containers: d3.ContainerElement[]): void => {
- if (this.dragArea) {
- this.dragArea.remove();
- }
- this.dragStartX = Math.max(0, this.getDragX(containers[0]) - this.margin.left);
- this.dragArea = this.svg.insert('rect', ':first-child').attr('x', this.dragStartX).attr('y', 0).attr('width', 0)
- .attr('height', this.height).attr('class', 'drag-area');
- })
- .on('drag', (datum: undefined, index: number, containers: d3.ContainerElement[]): void => {
- const mousePos = this.getDragX(containers[0]);
- const currentX = Math.max(mousePos, this.minDragX) - this.margin.left;
- const startX = Math.min(currentX, this.dragStartX);
- const currentWidth = Math.abs(currentX - this.dragStartX);
- this.dragArea.attr('x', startX).attr('width', currentWidth);
- let timeRange = this.getTimeRangeByXRanges(startX, startX + currentWidth);
- this.setChartTimeGap(new Date(timeRange[0]), new Date(timeRange[1]));
- })
- .on('end', (): void => {
- const dragAreaDetails = this.dragArea.node().getBBox();
- const startX = Math.max(0, dragAreaDetails.x);
- const endX = Math.min(this.width, dragAreaDetails.x + dragAreaDetails.width);
- const dateRange: [number, number] = this.getTimeRangeByXRanges(startX, endX);
- this.selectArea.emit(dateRange);
- this.dragArea.remove();
- this.setChartTimeGap(new Date(dateRange[0]), new Date(dateRange[1]));
- })
- );
- d3.selectAll(`svg#${this.svgId} .value, svg#${this.svgId} .axis`).call(d3.drag().on('start', (): void => {
- d3.event.sourceEvent.stopPropagation();
- }));
- }
-
- private getDragX(element: d3.ContainerElement): number {
- return d3.mouse(element)[0];
- }
-
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.html
new file mode 100644
index 0000000..e2ca744
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.html
@@ -0,0 +1,27 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<div #graphContainer></div>
+<footer *ngIf="firstDateTick || lastDateTick">
+ <div *ngIf="firstDateTick">{{firstDateTick | amTz: timeZone | amDateFormat: historyStartEndTimeFormat}}</div>
+ <div *ngIf="lastDateTick">{{lastDateTick | amTz: timeZone | amDateFormat: historyStartEndTimeFormat}}</div>
+</footer>
+<graph-legend class="col-md-12" [items]="legendItems"></graph-legend>
+<graph-tooltip #tooltip [data]="tooltipInfo.data" [ngClass]="{'hide': !tooltipInfo.data}"
+ [style.top]="tooltipInfo.data ? tooltipPosition.top + 'px' : ''"
+ [style.left]="tooltipInfo.data ? tooltipPosition.left + 'px' : ''"
+ [title]="tooltipInfo.title | amTz: timeZone | amDateFormat: tickTimeFormat"></graph-tooltip>
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.less
new file mode 100644
index 0000000..5f14eee
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.less
@@ -0,0 +1,42 @@
+/**
+ * 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 {
+ /deep/ .line {
+ fill: none;
+ stroke-width: 1.5px;
+ }
+
+ /deep/ .grid-line-area {
+ fill: transparent;
+
+ + .grid-line {
+ stroke: transparent;
+ }
+
+ &.visible-grid-line-area {
+ cursor: pointer;
+
+ + .grid-line {
+ stroke: @base-font-color;
+ }
+ }
+ }
+}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.spec.ts
new file mode 100644
index 0000000..b58717b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.spec.ts
@@ -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 {Injector, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
+import {MomentModule} from 'angular2-moment';
+import {MomentTimezoneModule} from 'angular-moment-timezone';
+import {TranslationModules} from '@app/test-config.spec';
+import {StoreModule} from '@ngrx/store';
+import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {UtilsService} from '@app/services/utils.service';
+import {ServiceInjector} from '@app/classes/service-injector';
+import {GraphLegendComponent} from '@app/components/graph-legend/graph-legend.component';
+
+import {TimeLineGraphComponent} from './time-line-graph.component';
+
+describe('TimeLineGraphComponent', () => {
+ let component: TimeLineGraphComponent;
+ let fixture: ComponentFixture<TimeLineGraphComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ TimeLineGraphComponent,
+ GraphLegendComponent
+ ],
+ imports: [
+ MomentModule,
+ MomentTimezoneModule,
+ ...TranslationModules,
+ StoreModule.provideStore({
+ appSettings
+ })
+ ],
+ providers: [
+ UtilsService,
+ AppSettingsService
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(inject([Injector], (injector: Injector) => {
+ ServiceInjector.injector = injector;
+ fixture = TestBed.createComponent(TimeLineGraphComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ }));
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.ts
new file mode 100644
index 0000000..2f0b450
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-line-graph/time-line-graph.component.ts
@@ -0,0 +1,111 @@
+/**
+ * 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 * as d3 from 'd3';
+import {GraphScaleItem, GraphLinePoint, GraphLineData} from '@app/classes/graph';
+import {TimeGraphComponent} from '@app/classes/components/graph/time-graph.component';
+
+@Component({
+ selector: 'time-line-graph',
+ templateUrl: './time-line-graph.component.html',
+ styleUrls: [
+ '../../classes/components/graph/graph.component.less', '../../classes/components/graph/time-graph.component.less',
+ './time-line-graph.component.less'
+ ]
+})
+export class TimeLineGraphComponent extends TimeGraphComponent {
+
+ @Input()
+ pointRadius: number = 3.5;
+
+ protected populate(): void {
+ const keys = Object.keys(this.colors),
+ data = this.data,
+ timeStamps = Object.keys(data),
+ dataForDomain = timeStamps.map((timeStamp: string): GraphScaleItem => Object.assign({
+ tick: Number(timeStamp)
+ }, data[timeStamp])),
+ dataForSvg = keys.map((key: string): GraphLineData => {
+ return {
+ points: timeStamps.map((timeStamp: string): GraphScaleItem => {
+ return {
+ tick: Number(timeStamp),
+ y: data[timeStamp][key]
+ };
+ }),
+ key: key
+ };
+ }),
+ line = d3.line<GraphScaleItem>().x(item => this.xScale(item.tick)).y(item => this.yScale(item.y));
+
+ // after we have the data we set the domain values both scales
+ this.setXScaleDomain(dataForDomain);
+ this.setYScaleDomain();
+
+ // drawing the axis
+ this.drawXAxis();
+ this.drawYAxis();
+
+ // populate the data and drawing the lines and points
+ const layer = this.svg.selectAll().data(dataForSvg);
+ layer.enter().append('path')
+ .attr('class', 'line').attr('d', (item: GraphLineData) => line(item.points))
+ .style('stroke', (item: GraphLineData): string => this.colors[item.key]);
+ layer.enter().append('g').selectAll('circle')
+ .data((item: GraphLineData): GraphLinePoint[] => item.points.map((point: GraphScaleItem): GraphLinePoint => {
+ return Object.assign({}, point, {
+ color: this.colors[item.key]
+ });
+ }))
+ .enter().append('circle')
+ .attr('cx', (item: GraphLinePoint): number => this.xScale(item.tick))
+ .attr('cy', (item: GraphLinePoint): number => this.yScale(item.y))
+ .attr('r', this.pointRadius)
+ .style('fill', (item: GraphLinePoint): string => item.color);
+ const gridLinesParent = this.svg.selectAll().data(dataForDomain).enter().append('g').selectAll()
+ .data((item: GraphScaleItem): GraphScaleItem[] => [item]).enter();
+ gridLinesParent.append('rect').attr('class', 'grid-line-area')
+ .attr('x', (item: GraphScaleItem): number => this.xScale(item.tick) - this.pointRadius).attr('y', 0)
+ .style('width', `${this.pointRadius * 2}px`).style('height', `${this.height}px`)
+ .on('mouseover', (d: GraphScaleItem, index: number, elements: HTMLElement[]): void => {
+ elements.forEach((element: HTMLElement) => element.classList.add('visible-grid-line-area'));
+ this.handleMouseOver(Object.assign([], d, {
+ data: d
+ }), index, elements);
+ })
+ .on('mousemove', this.handleMouseMove)
+ .on('mouseout', (d: GraphScaleItem, index: number, elements: HTMLElement[]): void => {
+ elements.forEach((element: HTMLElement) => element.classList.remove('visible-grid-line-area'));
+ this.handleMouseOut();
+ });
+ gridLinesParent.append('line').attr('class', 'grid-line')
+ .attr('x1', (item: GraphScaleItem): number => this.xScale(item.tick))
+ .attr('x2', (item: GraphScaleItem): number => this.xScale(item.tick))
+ .attr('y1', 0).attr('y2', this.height);
+ this.setDragBehavior();
+ }
+
+ protected setYScaleDomain(): void {
+ const keys = Object.keys(this.data),
+ maxValues = keys.map((currentKey: string): number => this.utils.getMaxNumberInObject(this.data[currentKey]), 0),
+ maximum = Math.max(...maxValues);
+ this.yScale.domain([0, maximum]);
+ }
+
+}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts
index 43e4bd5..e3034b0 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts
@@ -27,9 +27,12 @@
import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
-import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
+import {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {HttpClientService} from '@app/services/http-client.service';
@@ -61,6 +64,7 @@
hosts,
auditLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogs,
serviceLogsFields,
serviceLogsHistogramData,
@@ -82,6 +86,7 @@
HostsService,
AuditLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
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 ab56589..1772ec0 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
@@ -26,9 +26,12 @@
import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
-import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
+import {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {ComponentActionsService} from '@app/services/component-actions.service';
@@ -68,6 +71,7 @@
hosts,
auditLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogs,
serviceLogsFields,
serviceLogsHistogramData,
@@ -84,6 +88,7 @@
HostsService,
AuditLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
index ba5b613..ce4fa1c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
@@ -24,6 +24,7 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
import {
ServiceLogsHistogramDataService, serviceLogsHistogramData
@@ -60,6 +61,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
serviceLogsTruncated,
@@ -82,6 +84,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
index aa86f4f..1e1246b 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
@@ -128,28 +128,44 @@
{
dataCount: [
{
- name: 'n0',
- value: 1
+ name: currentTime.toISOString(),
+ value: '75'
},
{
- name: 'n1',
- value: 2
+ name: currentTime.clone().subtract(20, 'm').toISOString(),
+ value: '100'
+ },
+ {
+ name: currentTime.clone().subtract(40, 'm').toISOString(),
+ value: '75'
+ },
+ {
+ name: currentTime.clone().subtract(1, 'h').toISOString(),
+ value: '50'
}
],
- name: 'graph0'
+ name: 'AMBARI'
},
{
dataCount: [
{
- name: 'n2',
- value: 10
+ name: currentTime.toISOString(),
+ value: '150'
},
{
- name: 'n3',
- value: 20
+ name: currentTime.clone().subtract(20, 'm').toISOString(),
+ value: '50'
+ },
+ {
+ name: currentTime.clone().subtract(40, 'm').toISOString(),
+ value: '75'
+ },
+ {
+ name: currentTime.clone().subtract(1, 'h').toISOString(),
+ value: '100'
}
],
- name: 'graph1'
+ name: 'HDFS'
}
]
},
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 c2cee8d..952c542 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
@@ -27,8 +27,11 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.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 {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {HttpClientService} from '@app/services/http-client.service';
@@ -59,6 +62,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
serviceLogsTruncated,
@@ -76,6 +80,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
index 3f65cd1..a8ab5e8 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
@@ -23,8 +23,11 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.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 {
+ 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';
@@ -53,6 +56,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
appSettings,
@@ -75,6 +79,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
AppSettingsService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts
index 6f98bf5..1650d90 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts
@@ -24,6 +24,7 @@
} from '@angular/http';
import {HomogeneousObject} from '@app/classes/object';
import {AuditLogsListQueryParams} from '@app/classes/queries/audit-logs-query-params';
+import {AuditLogsGraphQueryParams} from '@app/classes/queries/audit-logs-graph-query-params';
import {AuditLogsTopResourcesQueryParams} from '@app/classes/queries/audit-logs-top-resources-query-params';
import {ServiceLogsQueryParams} from '@app/classes/queries/service-logs-query-params';
import {ServiceLogsHistogramQueryParams} from '@app/classes/queries/service-logs-histogram-query-params';
@@ -47,6 +48,10 @@
url: 'audit/logs',
params: opts => new AuditLogsListQueryParams(opts)
},
+ auditLogsGraph: {
+ url: 'audit/logs/bargraph',
+ params: opts => new AuditLogsGraphQueryParams(opts)
+ },
auditLogsFields: {
url: 'audit/logs/schema/fields'
},
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
index acdc0c2..5961309 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
@@ -22,6 +22,7 @@
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 {AuditLogsGraphDataService, auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
import {
ServiceLogsHistogramDataService, serviceLogsHistogramData
@@ -55,6 +56,7 @@
auditLogs,
serviceLogs,
auditLogsFields,
+ auditLogsGraphData,
serviceLogsFields,
serviceLogsHistogramData,
appSettings,
@@ -71,6 +73,7 @@
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
+ AuditLogsGraphDataService,
ServiceLogsFieldsService,
ServiceLogsHistogramDataService,
AppSettingsService,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
index 78bcdb1..4c8f2a3 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
@@ -30,6 +30,7 @@
import {HttpClientService} from '@app/services/http-client.service';
import {AuditLogsService} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService} from '@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService} from '@app/services/storage/audit-logs-graph-data.service';
import {ServiceLogsService} from '@app/services/storage/service-logs.service';
import {ServiceLogsFieldsService} from '@app/services/storage/service-logs-fields.service';
import {ServiceLogsHistogramDataService} from '@app/services/storage/service-logs-histogram-data.service';
@@ -61,13 +62,13 @@
export class LogsContainerService {
constructor(
- private httpClient: HttpClientService, private appState: AppStateService,
- private appSettings: AppSettingsService, private auditLogsStorage: AuditLogsService,
- private auditLogsFieldsStorage: AuditLogsFieldsService, private serviceLogsStorage: ServiceLogsService,
- private serviceLogsFieldsStorage: ServiceLogsFieldsService, private tabsStorage: TabsService,
+ private httpClient: HttpClientService, private tabsStorage: TabsService, private componentsStorage: ComponentsService,
+ private hostsStorage: HostsService, private appState: AppStateService, private auditLogsStorage: AuditLogsService,
+ private auditLogsGraphStorage: AuditLogsGraphDataService, private auditLogsFieldsStorage: AuditLogsFieldsService,
+ private serviceLogsStorage: ServiceLogsService, private serviceLogsFieldsStorage: ServiceLogsFieldsService,
private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private clustersStorage: ClustersService,
- private componentsStorage: ComponentsService, private hostsStorage: HostsService,
- private serviceLogsTruncatedStorage: ServiceLogsTruncatedService
+ private serviceLogsTruncatedStorage: ServiceLogsTruncatedService, private appSettings: AppSettingsService
+
) {
const formItems = Object.keys(this.filters).reduce((currentObject: any, key: string): HomogeneousObject<FormControl> => {
let formControl = new FormControl(),
@@ -498,7 +499,7 @@
query: ['includeQuery', 'excludeQuery']
};
- private readonly histogramFilters = {
+ private readonly graphFilters = {
clusters: ['clusters'],
timeRange: ['to', 'from'],
components: ['mustBe'],
@@ -518,7 +519,9 @@
// TODO add all the required fields
listFilters: ['clusters', 'timeRange', 'auditLogsSorting', 'pageSize', 'page', 'query'],
topResourcesFilters: ['clusters', 'timeRange', 'query'],
- histogramFilters: ['clusters', 'timeRange', 'query']
+ graphFilters: ['clusters', 'timeRange', 'query'],
+ graphRequestName: 'auditLogsGraph',
+ graphModel: this.auditLogsGraphStorage
},
serviceLogs: {
logsModel: this.serviceLogsStorage,
@@ -526,7 +529,9 @@
listFilters: [
'clusters', 'timeRange', 'components', 'levels', 'hosts', 'serviceLogsSorting', 'pageSize', 'page', 'query'
],
- histogramFilters: ['clusters', 'timeRange', 'components', 'levels', 'hosts', 'query']
+ graphFilters: ['clusters', 'timeRange', 'components', 'levels', 'hosts', 'query'],
+ graphRequestName: 'serviceLogsHistogram',
+ graphModel: this.serviceLogsHistogramStorage
}
};
@@ -645,19 +650,17 @@
this.totalCount = count;
}
});
- if (logsType === 'serviceLogs') {
- // TODO rewrite to implement conditional data loading for service logs histogram or audit logs graph
- this.httpClient.get('serviceLogsHistogram', this.getParams('histogramFilters')).subscribe((response: Response): void => {
- const jsonResponse = response.json();
- this.serviceLogsHistogramStorage.clear();
- if (jsonResponse) {
- const histogramData = jsonResponse.graphData;
- if (histogramData) {
- this.serviceLogsHistogramStorage.addInstances(histogramData);
- }
+ this.httpClient.get(this.logsTypeMap[logsType].graphRequestName, this.getParams('graphFilters')).subscribe((response: Response): void => {
+ const jsonResponse = response.json(),
+ model = this.logsTypeMap[logsType].graphModel;
+ model.clear();
+ if (jsonResponse) {
+ const graphData = jsonResponse.graphData;
+ if (graphData) {
+ model.addInstances(graphData);
}
- });
- }
+ }
+ });
if (logsType === 'auditLogs') {
this.httpClient.get('topAuditLogsResources', this.getParams('topResourcesFilters', {
field: 'resource'
@@ -752,21 +755,23 @@
return Object.assign({}, params, additionalParams);
}
- getHistogramData(data: BarGraph[]): HomogeneousObject<HomogeneousObject<number>> {
- let histogramData = {};
+ getGraphData(data: BarGraph[], keys?: string[]): HomogeneousObject<HomogeneousObject<number>> {
+ let graphData = {};
data.forEach(type => {
const name = type.name;
type.dataCount.forEach(entry => {
const timeStamp = new Date(entry.name).valueOf();
- if (!histogramData[timeStamp]) {
+ if (!graphData[timeStamp]) {
let initialValue = {};
- Object.keys(this.colors).forEach(key => initialValue[key] = 0);
- histogramData[timeStamp] = initialValue;
+ if (keys) {
+ keys.forEach((key: string) => initialValue[key] = 0);
+ }
+ graphData[timeStamp] = initialValue;
}
- histogramData[timeStamp][name] = Number(entry.value);
+ graphData[timeStamp][name] = Number(entry.value);
});
});
- return histogramData;
+ return graphData;
}
loadColumnsNames(): void {
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/audit-logs-graph-data.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/audit-logs-graph-data.service.ts
new file mode 100644
index 0000000..eeb2780
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/audit-logs-graph-data.service.ts
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Injectable} from '@angular/core';
+import {Store} from '@ngrx/store';
+import {AppStore, CollectionModelService, getCollectionReducer} from '@app/classes/models/store';
+
+export const modelName = 'auditLogsGraphData';
+
+@Injectable()
+export class AuditLogsGraphDataService extends CollectionModelService {
+ constructor(store: Store<AppStore>) {
+ super(modelName, store);
+ }
+}
+
+export const auditLogsGraphData = getCollectionReducer(modelName);
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts
index d700b16..3f54625 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/storage/reducers.service.ts
@@ -30,6 +30,7 @@
import {serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
import {serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
import {auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {auditLogsGraphData} from '@app/services/storage/audit-logs-graph-data.service';
import {userConfigs} from '@app/services/storage/user-configs.service';
import {tabs} from '@app/services/storage/tabs.service';
@@ -37,6 +38,7 @@
appSettings,
appState,
auditLogs,
+ auditLogsGraphData,
serviceLogs,
serviceLogsHistogramData,
serviceLogsTruncated,
diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
index 48c1d88..923160a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
+++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
@@ -161,6 +161,7 @@
"logs.showGraph": "Show Graph",
"logs.topUsers": "Top {{number}} Users",
"logs.topResources": "Top {{number}} Resources",
+ "logs.duration": "Duration",
"histogram.gap": "gap",
"histogram.gaps": "gaps",