add limit by
This commit is contained in:
@@ -11,11 +11,15 @@ import { HelpPageComponent } from './help-page/help-page.component';
|
||||
import { UploadPageComponent } from './upload-page/upload-page.component';
|
||||
import { VisualizationPageComponent } from './visualization-page/visualization-page.component';
|
||||
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatAutocompleteModule} from '@angular/material/autocomplete';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatFormFieldModule, MatInputModule} from '@angular/material';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import { YAxisRangeComponent } from './y-axis-range/y-axis-range.component';
|
||||
import { QueryAutocompleteComponent } from './query-autocomplete/query-autocomplete.component';
|
||||
import { LimitByComponent } from './limit-by/limit-by.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -26,14 +30,20 @@ import { QueryAutocompleteComponent } from './query-autocomplete/query-autocompl
|
||||
VisualizationPageComponent,
|
||||
YAxisRangeComponent,
|
||||
QueryAutocompleteComponent,
|
||||
LimitByComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatSelectModule,MatFormFieldModule, MatInputModule,
|
||||
MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
MatTooltipModule,
|
||||
BrowserAnimationsModule,
|
||||
HttpClientModule
|
||||
],
|
||||
|
||||
@@ -13,14 +13,37 @@ This page describes how to use this tool.
|
||||
|
||||
<h2>Why not use one of the existing monitoring tools?</h2>
|
||||
<p>
|
||||
There are many tools out there that do monitoring in one way or the other. Here are a few:
|
||||
There are many tools out there that do monitoring in one way or the other. There are complete closed source solutions like
|
||||
<a href="https://newrelic.com" target="_blank" rel="nofollow" class="external-link">New Relic</a>,
|
||||
<a href="https://www.splunk.com" target="_blank" rel="nofollow" class="external-link">Splunk</a>,
|
||||
and <a href="https://www.appdynamics.com/" target="_blank" rel="nofollow" class="external-link">App Dynamics</a>.
|
||||
They bring everything you need, a tool to collect logs or metrics, a database, a UI with dashboards and alerting.
|
||||
</p>
|
||||
<p>
|
||||
The OSS community likes to break them down into separate tools.
|
||||
For example the <a href="https://www.elastic.co/products/elastic-stack" target="_blank" rel="nofollow" class="external-link">ELK</a>-stack
|
||||
consists of <a href="https://www.elastic.co/products/elasticsearch" target="_blank" rel="nofollow" class="external-link">Elasticsearch</a>
|
||||
as database, and <a href="https://www.elastic.co/products/kibana" target="_blank" rel="nofollow" class="external-link">Kibana</a>
|
||||
as the UI. It is usually combined with <a href="https://www.elastic.co/products/logstash" target="_blank" rel="nofollow" class="external-link">Logstash</a>
|
||||
for logfile collection.<br/>
|
||||
Another popular UI is <a href="https://grafana.com/" target="_blank" rel="nofollow" class="external-link">Grafana</a>
|
||||
which can be combined with
|
||||
<a href="https://grafana.com/oss/loki/" target="_blank" rel="nofollow" class="external-link">Loki</a>,
|
||||
<a href="https://prometheus.io/" target="_blank" rel="nofollow" class="external-link">Prometheus</a>,
|
||||
<a href="https://graphiteapp.org/" target="_blank" rel="nofollow" class="external-link">Graphite</a> or
|
||||
<a href="https://grafana.com/oss/metrictank/" target="_blank" rel="nofollow" class="external-link">Metrictank</a> (which is an engine for Graphite).
|
||||
|
||||
<br/>
|
||||
<br/><br/>
|
||||
<a href="https://www.elastic.co/products/kibana" target="_blank" rel="nofollow" class="external-link">Kibana</a> (<a href="https://www.elastic.co/products/elastic-stack" target="_blank" rel="nofollow" class="external-link">ELK</a>),
|
||||
<a href="https://www.appdynamics.com/" target="_blank" rel="nofollow" class="external-link">App Dynamics</a>,
|
||||
<a href="https://graphiteapp.org/" target="_blank" rel="nofollow" class="external-link">Graphite</a>,
|
||||
<a href="https://prometheus.io/" target="_blank" rel="nofollow" class="external-link">Prometheus</a>
|
||||
+ <a href="https://grafana.com/" target="_blank" rel="nofollow" class="external-link">Grafana</a>,
|
||||
<a href="https://graphiteapp.org/" target="_blank" rel="nofollow" class="external-link">Graphite</a>
|
||||
(<a href="https://grafana.com/oss/metrictank/" target="_blank" rel="nofollow" class="external-link">Metrictank</a>),
|
||||
<a href="https://prometheus.io/" target="_blank" rel="nofollow" class="external-link">Prometheus</a>,
|
||||
<a href="https://grafana.com/oss/loki/" target="_blank" rel="nofollow" class="external-link">Loki</a>,
|
||||
<a href="https://grafana.com/" target="_blank" rel="nofollow" class="external-link">Grafana</a>,
|
||||
|
||||
|
||||
<a href="https://github.com/Netflix/atlas" target="_blank" rel="nofollow" class="external-link">Netflix Atlas</a>,
|
||||
<a href="https://oss.oetiker.ch/rrdtool/" target="_blank" rel="nofollow" class="external-link">RRDtool</a>,
|
||||
<a href="http://ganglia.sourceforge.net/" target="_blank" rel="nofollow" class="external-link">Ganglia</a>,
|
||||
@@ -38,6 +61,7 @@ There are many tools out there that do monitoring in one way or the other. Here
|
||||
<a href="https://www.site24x7.com/" target="_blank" rel="nofollow" class="external-link">Site24x7</a>,
|
||||
<a href="https://www.datadoghq.com/" target="_blank" rel="nofollow" class="external-link">Datadog</a>,
|
||||
<a href="https://www.microfocus.com/en-us/products/sitescope-application-monitoring/overview" target="_blank" rel="nofollow" class="external-link">Sitescope</a>,
|
||||
<a href="https://www.signalfx.com/" target="_blank" rel="nofollow" class="external-link">SignalFX</a>,
|
||||
and many more. None of them provides the visualizations we had in mind. We wanted to plot each value of the time series data individually, so that we can identify the
|
||||
response times of a single request. But tools like Splunk, Kibana, Chronograf or Grafana only plot aggregated data (average, min/max, percentiles).
|
||||
<p>
|
||||
|
||||
22
pdb-js/src/app/limit-by/limit-by.component.html
Normal file
22
pdb-js/src/app/limit-by/limit-by.component.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<mat-form-field id="limitBy">
|
||||
<mat-label>Limit By:</mat-label>
|
||||
<mat-select [(value)]="limitBy">
|
||||
<mat-option value="NO_LIMIT">no limit</mat-option>
|
||||
<mat-option value="MOST_VALUES">most values</mat-option>
|
||||
<mat-option value="FEWEST_VALUES">fewest values</mat-option>
|
||||
<mat-option value="MAX_VALUE">max value</mat-option>
|
||||
<mat-option value="MIN_VALUE">min value</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field id="limit"
|
||||
*ngIf="limitBy !== 'NO_LIMIT'">
|
||||
<input
|
||||
matInput
|
||||
type="number"
|
||||
placeholder="Limit"
|
||||
min="1"
|
||||
value="{{limit}}"
|
||||
|
||||
>
|
||||
</mat-form-field>
|
||||
9
pdb-js/src/app/limit-by/limit-by.component.scss
Normal file
9
pdb-js/src/app/limit-by/limit-by.component.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
#limitBy {
|
||||
width: 10em;
|
||||
margin-right: 1ex;
|
||||
}
|
||||
|
||||
#limit {
|
||||
width: 5em;
|
||||
margin-right: 0ex;
|
||||
}
|
||||
25
pdb-js/src/app/limit-by/limit-by.component.spec.ts
Normal file
25
pdb-js/src/app/limit-by/limit-by.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LimitByComponent } from './limit-by.component';
|
||||
|
||||
describe('LimitByComponent', () => {
|
||||
let component: LimitByComponent;
|
||||
let fixture: ComponentFixture<LimitByComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LimitByComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LimitByComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
18
pdb-js/src/app/limit-by/limit-by.component.ts
Normal file
18
pdb-js/src/app/limit-by/limit-by.component.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Component, OnInit, Input} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'pdb-limit-by',
|
||||
templateUrl: './limit-by.component.html',
|
||||
styleUrls: ['./limit-by.component.scss']
|
||||
})
|
||||
export class LimitByComponent implements OnInit {
|
||||
|
||||
@Input() limit: number;
|
||||
|
||||
@Input() limitBy: string;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ export class PlotService {
|
||||
this.plotTypes.push(new PlotType("Bar", "bar-chart", false, DataType.Other, DataType.Other));
|
||||
|
||||
this.tagFields = new Array<TagField>();
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
placeholder="Query"
|
||||
[formControl]="query"
|
||||
[matAutocomplete]="auto"
|
||||
(keyup)="onKey($event)"/>
|
||||
(keyup)="onKey($event)"
|
||||
(focus)="onKey($event)"/>
|
||||
<mat-autocomplete
|
||||
#auto="matAutocomplete"
|
||||
[displayWith]="displaySuggestion"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.mat-option {
|
||||
/*height: 1.5em;
|
||||
line-height: 1.5em;
|
||||
/**/
|
||||
}
|
||||
|
||||
#query-autocomplete-input {
|
||||
border: solid 1px #ccc;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
padding: 5px;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import { PlotService, PlotType, AutocompleteResult, Suggestion } from '../plot.s
|
||||
})
|
||||
export class QueryAutocompleteComponent implements OnInit {
|
||||
|
||||
@Input() query = new FormControl();
|
||||
@Input() query = new FormControl('');
|
||||
|
||||
suggestions = new FormControl();
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<div id="visualization">
|
||||
<div id="query-box">
|
||||
<!-- [query]="query"-->
|
||||
<pdb-query-autocomplete ></pdb-query-autocomplete>
|
||||
</div>
|
||||
<div id="filters">
|
||||
<div id="filterpanel">
|
||||
<mat-form-field class="mat-field-full-width">
|
||||
<mat-label>Date Range:</mat-label>
|
||||
<input matInput [formControl]="dateRange" name="dates" />
|
||||
@@ -35,18 +30,15 @@
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Group By:</mat-label>
|
||||
<mat-select multiple>
|
||||
<mat-select multiple [(value)]="groupBy">
|
||||
<mat-option *ngFor="let tagField of tagFields" [value]="tagField">{{tagField.name}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Split By:</mat-label>
|
||||
<mat-select>
|
||||
<mat-option>-</mat-option>
|
||||
<mat-option *ngFor="let tagField of tagFields" [value]="tagField">{{tagField.name}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<pdb-limit-by
|
||||
[(limitBy)]="limitBy"
|
||||
[limit]="limit"
|
||||
></pdb-limit-by>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Y-Axis:</mat-label>
|
||||
@@ -63,18 +55,38 @@
|
||||
></pdb-y-axis-range>
|
||||
|
||||
|
||||
<div>
|
||||
<button mat-button (click)="plot()">
|
||||
<mat-checkbox [(ngModel)]="enableGallery">Gallery</mat-checkbox>
|
||||
|
||||
<mat-form-field *ngIf="enableGallery">
|
||||
<mat-label>Split By:</mat-label>
|
||||
<mat-select [(value)]="splitBy">
|
||||
<mat-option *ngFor="let tagField of tagFields" [value]="tagField">{{tagField.name}}</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="splitBy == null || true">
|
||||
Please select a value!
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
<div id="plot-button-bar">
|
||||
<button
|
||||
*ngIf="!enableGallery"
|
||||
mat-button
|
||||
matTooltip="Create Plot"
|
||||
(click)="plot()">
|
||||
<img src="assets/img/scatter-chart2.svg" class="icon-inline" aria-hidden="true" title="create plot" />
|
||||
Plot
|
||||
</button>
|
||||
<!--
|
||||
<button mat-button (click)="gallery()">
|
||||
<button
|
||||
*ngIf="enableGallery"
|
||||
mat-button
|
||||
matTooltip="Create Gallery"
|
||||
(click)="plot()"
|
||||
[disabled]="this.splitBy == null">
|
||||
<img src="assets/img/four-squares-line.svg" class="icon-inline" aria-hidden="true" title="Create Gallery (only active if 'Split' is set)" />
|
||||
Gallery
|
||||
</button>
|
||||
-->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="results"></div>
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
grid:
|
||||
"query-box query-box" auto
|
||||
"filters results" 1fr
|
||||
/ 23em 3fr;
|
||||
/ 25em 3fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: 1000px) {
|
||||
#visualization {
|
||||
display: grid;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
grid:
|
||||
"query-box" auto
|
||||
@@ -29,22 +28,31 @@
|
||||
"results" 1fr
|
||||
/ 1fr;
|
||||
}
|
||||
#results {
|
||||
height: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
#query-box {
|
||||
grid-area: query-box;
|
||||
padding: 2px;
|
||||
border-bottom: 1px solid black;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#filters {
|
||||
grid-area: filters;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
#filterpanel {
|
||||
background-color: #f8f8f8;/*#fafafa;*/
|
||||
padding: 1em;
|
||||
margin: 0 1em 1em 1em;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#results {
|
||||
grid-area: results;
|
||||
}
|
||||
|
||||
#plot-button-bar {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { PlotService, PlotType } from '../plot.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { FormControl, Validators } from '@angular/forms';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'pdb-visualization-page',
|
||||
@@ -22,6 +23,10 @@ export class VisualizationPageComponent implements OnInit {
|
||||
|
||||
tagFields: Array<any>;
|
||||
|
||||
groupBy: Array<any> = [];
|
||||
limitBy: string = "NO_LIMIT";
|
||||
limit = 10;
|
||||
|
||||
yAxis: string;
|
||||
yAxisUnit: string;
|
||||
minYValue: number;
|
||||
@@ -29,6 +34,12 @@ export class VisualizationPageComponent implements OnInit {
|
||||
|
||||
query: string;
|
||||
|
||||
|
||||
enableGallery = true;
|
||||
splitBy = new FormControl(null, [
|
||||
Validators.required
|
||||
]);
|
||||
|
||||
constructor(private plotService: PlotService) {
|
||||
}
|
||||
|
||||
@@ -48,23 +59,37 @@ export class VisualizationPageComponent implements OnInit {
|
||||
this.minYValue = 0;
|
||||
this.maxYValue = 120;
|
||||
|
||||
|
||||
this.selectedPlotType.valueChanges.subscribe(function(selectedMainPlotType){
|
||||
that.combinePlotTypes = that.getCombinablePlotTypes(selectedMainPlotType);
|
||||
});
|
||||
}
|
||||
|
||||
getCombinablePlotTypes(selectedMainPlotType) : Array<any>{
|
||||
// get compatible plot types
|
||||
const mainPlotType = this.availablePlotTypes[selectedMainPlotType];
|
||||
|
||||
const compatiblePlotTypes = this.plotTypes.filter(pt => pt.compatible(mainPlotType));
|
||||
console.log(compatiblePlotTypes);
|
||||
return compatiblePlotTypes;
|
||||
}
|
||||
|
||||
plot(){
|
||||
console.log("plot clicked");
|
||||
|
||||
var request = {};
|
||||
request['query'] = this.query;
|
||||
request['height'] = document.getElementById("results").offsetHeight;
|
||||
request['width'] = document.getElementById("results").offsetWidth;
|
||||
request['groupBy'] = this.groupBy.map(o => o.name);
|
||||
request['limitBy'] = this.limitBy;
|
||||
request['limit'] = this.limit;
|
||||
request['dateRange'] = this.dateRange.value;
|
||||
request['axisScale'] = this.yAxis;
|
||||
request['aggregate'] = this.selectedCombinePlotType.value;
|
||||
request['keyOutside'] = false;
|
||||
request['generateThumbnail'] = this.enableGallery;
|
||||
request['yRangeMin'] = this.minYValue;
|
||||
request['yRangeMax'] = this.maxYValue;
|
||||
request['yRangeUnit'] = this.yAxisUnit;
|
||||
|
||||
console.log("plot clicked: "+ JSON.stringify(request));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<input matInput type="number" placeholder="Min Y-Value" min="0" value="{{minYValue}}">
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<input matInput type="number" placeholder="Max Y-Value" min="0" value="{{maxYValue}}">
|
||||
</mat-form-field>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="yAxisUnit">
|
||||
<mat-label>Unit:</mat-label>
|
||||
<mat-select [(value)]="yAxisUnit">
|
||||
@@ -14,4 +15,5 @@
|
||||
<mat-option value="HOURS">hours</mat-option>
|
||||
<mat-option value="DAYS">days</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -35,6 +35,10 @@ body {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
button[disabled] .icon-inline {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.icon-small {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
@@ -65,6 +69,9 @@ a.external-link:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
body .mat-select-panel {
|
||||
@@ -78,3 +85,5 @@ body .mat-select-panel {
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user