add limit by

This commit is contained in:
2019-10-19 08:48:21 +02:00
parent a3099d4981
commit 24bf7c98e3
15 changed files with 286 additions and 110 deletions

View File

@@ -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
],

View File

@@ -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>

View 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>

View File

@@ -0,0 +1,9 @@
#limitBy {
width: 10em;
margin-right: 1ex;
}
#limit {
width: 5em;
margin-right: 0ex;
}

View 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();
});
});

View 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() {
}
}

View File

@@ -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() {

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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));
}
}

View File

@@ -1,3 +1,4 @@
<div>
<mat-form-field>
<input matInput type="number" placeholder="Min Y-Value" min="0" value="{{minYValue}}">
</mat-form-field>
@@ -15,3 +16,4 @@
<mat-option value="DAYS">days</mat-option>
</mat-select>
</mat-form-field>
</div>

View File

@@ -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%;
}