Merge pull request #1 from grafana/master

Update local master
This commit is contained in:
Pavel
2018-11-29 12:40:55 +02:00
committed by GitHub
14 changed files with 162 additions and 62 deletions
+3 -4
View File
@@ -34,7 +34,7 @@ protocol = http
# The ip address to bind to, empty will bind to all interfaces
http_addr =
# The http port to use
# The http port to use
http_port = 3000
# The public facing domain name used to access grafana from a browser
@@ -166,7 +166,7 @@ google_tag_manager_id =
# default admin user, created on startup
admin_user = admin
# default admin password, can be changed before first start of grafana, or in profile settings
# default admin password, can be changed before first start of grafana, or in profile settings
admin_password = admin
# used for signing
@@ -372,7 +372,7 @@ templates_pattern = emails/*.html
#################################### Logging ##########################
[log]
# Either "console", "file", "syslog". Default is console and file
# Either "console", "file", "syslog". Default is console and file
# Use space to separate multiple modes, e.g. "console file"
mode = console file
@@ -565,4 +565,3 @@ enable_alpha = false
[enterprise]
license_path =
+3
View File
@@ -145,6 +145,9 @@ log_queries =
# Google Analytics universal tracking code, only enabled if you specify an id here
;google_analytics_ua_id =
# Google Tag Manager ID, only enabled if you specify an id here
;google_tag_manager_id =
#################################### Security ####################################
[security]
# default admin user, created on startup
+2
View File
@@ -107,3 +107,5 @@ it as usual and then update the data source option in the metrics tab so that th
data source. Another alternative is to open the json file in a a text editor and update the data source properties
to value that matches a name of your data source.
## Note
In Grafana v5.3.4+ the export modal has new checkbox for sharing for external use (other instances). If the checkbox is not checked then the `__inputs` section will not be included in the exported JSON file.
+6
View File
@@ -428,10 +428,16 @@ kbn.valueFormats.hex0x = (value, decimals) => {
};
kbn.valueFormats.sci = (value, decimals) => {
if (value == null) {
return '';
}
return value.toExponential(decimals);
};
kbn.valueFormats.locale = (value, decimals) => {
if (value == null) {
return '';
}
return value.toLocaleString(undefined, { maximumFractionDigits: decimals });
};
+51 -4
View File
@@ -97,6 +97,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
* Local ID cache to compare requested vs selected datasource
*/
requestedDatasourceId: string;
scanTimer: NodeJS.Timer;
/**
* Timepicker to control scanning
*/
timepickerRef: React.RefObject<TimePicker>;
constructor(props) {
super(props);
@@ -122,6 +127,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
history: [],
queryTransactions: [],
range: initialRange,
scanning: false,
showingGraph: true,
showingLogs: true,
showingStartPage: false,
@@ -132,6 +138,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
};
}
this.modifiedQueries = initialQueries.slice();
this.timepickerRef = React.createRef();
}
async componentDidMount() {
@@ -164,6 +171,10 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}
}
componentWillUnmount() {
clearTimeout(this.scanTimer);
}
async setDatasource(datasource: any, origin?: DataSource) {
const supportsGraph = datasource.meta.metrics;
const supportsLogs = datasource.meta.logs;
@@ -317,11 +328,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}
};
onChangeTime = (nextRange: RawTimeRange) => {
onChangeTime = (nextRange: RawTimeRange, scanning?: boolean) => {
const range: RawTimeRange = {
...nextRange,
};
this.setState({ range }, () => this.onSubmit());
if (this.state.scanning && !scanning) {
this.onStopScanning();
}
this.setState({ range, scanning }, () => this.onSubmit());
};
onClickClear = () => {
@@ -496,6 +510,24 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
);
};
onStartScanning = () => {
this.setState({ scanning: true }, this.scanPreviousRange);
};
scanPreviousRange = () => {
const scanRange = this.timepickerRef.current.move(-1, true);
this.setState({ scanRange });
};
onStopScanning = () => {
clearTimeout(this.scanTimer);
this.setState(state => {
const { queryTransactions } = state;
const nextQueryTransactions = queryTransactions.filter(qt => qt.scanning && !qt.done);
return { queryTransactions: nextQueryTransactions, scanning: false, scanRange: undefined };
});
};
onSubmit = () => {
const { showingLogs, showingGraph, showingTable, supportsGraph, supportsLogs, supportsTable } = this.state;
// Keep table queries first since they need to return quickly
@@ -563,6 +595,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
done: false,
latency: 0,
options: queryOptions,
scanning: this.state.scanning,
};
// Using updater style because we might be modifying queryTransactions in quick succession
@@ -599,7 +632,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}
this.setState(state => {
const { history, queryTransactions } = state;
const { history, queryTransactions, scanning } = state;
// Transaction might have been discarded
const transaction = queryTransactions.find(qt => qt.id === transactionId);
@@ -629,6 +662,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
const nextHistory = updateHistory(history, datasourceId, queries);
// Keep scanning for results if this was the last scanning transaction
if (_.size(result) === 0 && scanning) {
const other = nextQueryTransactions.find(qt => qt.scanning && !qt.done);
if (!other) {
this.scanTimer = setTimeout(this.scanPreviousRange, 1000);
}
}
return {
history: nextHistory,
queryTransactions: nextQueryTransactions,
@@ -740,6 +781,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
initialQueries,
queryTransactions,
range,
scanning,
scanRange,
showingGraph,
showingLogs,
showingStartPage,
@@ -822,7 +865,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
</button>
</div>
) : null}
<TimePicker range={range} onChangeTime={this.onChangeTime} />
<TimePicker ref={this.timepickerRef} range={range} onChangeTime={this.onChangeTime} />
<div className="navbar-buttons">
<button className="btn navbar-button navbar-button--no-icon" onClick={this.onClickClear}>
Clear All
@@ -898,7 +941,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
loading={logsLoading}
position={position}
onChangeTime={this.onChangeTime}
onStartScanning={this.onStartScanning}
onStopScanning={this.onStopScanning}
range={range}
scanning={scanning}
scanRange={scanRange}
/>
</Panel>
)}
+36 -2
View File
@@ -1,6 +1,7 @@
import React, { Fragment, PureComponent } from 'react';
import Highlighter from 'react-highlight-words';
import * as rangeUtil from 'app/core/utils/rangeutil';
import { RawTimeRange } from 'app/types/series';
import { LogsDedupStrategy, LogsModel, dedupLogRows, filterLogLevels, LogLevel } from 'app/core/logs_model';
import { findHighlightChunksInText } from 'app/core/utils/text';
@@ -28,7 +29,11 @@ interface LogsProps {
loading: boolean;
position: string;
range?: RawTimeRange;
scanning?: boolean;
scanRange?: RawTimeRange;
onChangeTime?: (range: RawTimeRange) => void;
onStartScanning?: () => void;
onStopScanning?: () => void;
}
interface LogsState {
@@ -83,8 +88,18 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
this.setState({ hiddenLogLevels });
};
onClickScan = (event: React.SyntheticEvent) => {
event.preventDefault();
this.props.onStartScanning();
};
onClickStopScan = (event: React.SyntheticEvent) => {
event.preventDefault();
this.props.onStopScanning();
};
render() {
const { className = '', data, loading = false, position, range } = this.props;
const { className = '', data, loading = false, position, range, scanning, scanRange } = this.props;
const { dedup, hiddenLogLevels, showLabels, showLocalTime, showUtc } = this.state;
const hasData = data && data.rows && data.rows.length > 0;
const filteredData = filterLogLevels(data, hiddenLogLevels);
@@ -111,6 +126,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
const logEntriesStyle = {
gridTemplateColumns: cssColumnSizes.join(' '),
};
const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...';
return (
<div className={`${className} logs`}>
@@ -200,7 +216,25 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
</Fragment>
))}
</div>
{!loading && !hasData && 'No data was returned.'}
{!loading &&
!hasData &&
!scanning && (
<div className="logs-nodata">
No logs found.
<a className="link" onClick={this.onClickScan}>
Scan for older logs
</a>
</div>
)}
{scanning && (
<div className="logs-nodata">
<span>{scanText}</span>
<a className="link" onClick={this.onClickStopScan}>
Stop scan
</a>
</div>
)}
</div>
);
}
+7 -4
View File
@@ -35,7 +35,7 @@ interface TimePickerProps {
isOpen?: boolean;
isUtc?: boolean;
range?: RawTimeRange;
onChangeTime?: (Range) => void;
onChangeTime?: (range: RawTimeRange, scanning?: boolean) => void;
}
interface TimePickerState {
@@ -92,12 +92,13 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
};
}
move(direction: number) {
move(direction: number, scanning?: boolean): RawTimeRange {
const { onChangeTime } = this.props;
const { fromRaw, toRaw } = this.state;
const from = dateMath.parse(fromRaw, false);
const to = dateMath.parse(toRaw, true);
const timespan = (to.valueOf() - from.valueOf()) / 2;
const step = scanning ? 1 : 2;
const timespan = (to.valueOf() - from.valueOf()) / step;
let nextTo, nextFrom;
if (direction === -1) {
@@ -127,9 +128,11 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
toRaw: nextRange.to.format(DATE_FORMAT),
},
() => {
onChangeTime(nextRange);
onChangeTime(nextRange, scanning);
}
);
return nextRange;
}
handleChangeFrom = e => {
@@ -2,22 +2,8 @@ import coreModule from 'app/core/core_module';
import _ from 'lodash';
import * as queryDef from './query_def';
export function elasticBucketAgg() {
return {
templateUrl: 'public/app/plugins/datasource/elasticsearch/partials/bucket_agg.html',
controller: 'ElasticBucketAggCtrl',
restrict: 'E',
scope: {
target: '=',
index: '=',
onChange: '&',
getFields: '&',
},
};
}
export class ElasticBucketAggCtrl {
/** @nginject */
/** @ngInject */
constructor($scope, uiSegmentSrv, $q, $rootScope) {
const bucketAggs = $scope.target.bucketAggs;
@@ -226,5 +212,18 @@ export class ElasticBucketAggCtrl {
}
}
export function elasticBucketAgg() {
return {
templateUrl: 'public/app/plugins/datasource/elasticsearch/partials/bucket_agg.html',
controller: ElasticBucketAggCtrl,
restrict: 'E',
scope: {
target: '=',
index: '=',
onChange: '&',
getFields: '&',
},
};
}
coreModule.directive('elasticBucketAgg', elasticBucketAgg);
coreModule.controller('ElasticBucketAggCtrl', ElasticBucketAggCtrl);
@@ -2,22 +2,8 @@ import coreModule from 'app/core/core_module';
import _ from 'lodash';
import * as queryDef from './query_def';
export function elasticMetricAgg() {
return {
templateUrl: 'public/app/plugins/datasource/elasticsearch/partials/metric_agg.html',
controller: 'ElasticMetricAggCtrl',
restrict: 'E',
scope: {
target: '=',
index: '=',
onChange: '&',
getFields: '&',
esVersion: '=',
},
};
}
export class ElasticMetricAggCtrl {
/** @ngInject */
constructor($scope, uiSegmentSrv, $q, $rootScope) {
const metricAggs = $scope.target.metrics;
$scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion);
@@ -209,5 +195,19 @@ export class ElasticMetricAggCtrl {
}
}
export function elasticMetricAgg() {
return {
templateUrl: 'public/app/plugins/datasource/elasticsearch/partials/metric_agg.html',
controller: ElasticMetricAggCtrl,
restrict: 'E',
scope: {
target: '=',
index: '=',
onChange: '&',
getFields: '&',
esVersion: '=',
},
};
}
coreModule.directive('elasticMetricAgg', elasticMetricAgg);
coreModule.controller('ElasticMetricAggCtrl', ElasticMetricAggCtrl);
@@ -35,4 +35,11 @@ describe('parseQuery', () => {
regexp: 'x|y',
});
});
it('returns query for selector with two labels', () => {
expect(parseQuery('{foo="bar", baz="42"}')).toEqual({
query: '{foo="bar", baz="42"}',
regexp: '',
});
});
});
@@ -16,26 +16,15 @@ const DEFAULT_QUERY_PARAMS = {
query: '',
};
const QUERY_REGEXP = /({\w+="[^"]+"})?\s*(\w[^{]+)?\s*({\w+="[^"]+"})?/;
const selectorRegexp = /{[^{]*}/g;
export function parseQuery(input: string) {
const match = input.match(QUERY_REGEXP);
const match = input.match(selectorRegexp);
let query = '';
let regexp = '';
let regexp = input;
if (match) {
if (match[1]) {
query = match[1];
}
if (match[2]) {
regexp = match[2].trim();
}
if (match[3]) {
if (match[1]) {
query = `${match[1].slice(0, -1)},${match[3].slice(1)}`;
} else {
query = match[3];
}
}
query = match[0];
regexp = input.replace(selectorRegexp, '').trim();
}
return { query, regexp };
+3
View File
@@ -140,6 +140,7 @@ export interface QueryTransaction {
result?: any; // Table model / Timeseries[] / Logs
resultType: ResultType;
rowIndex: number;
scanning?: boolean;
}
export interface TextMatch {
@@ -162,6 +163,8 @@ export interface ExploreState {
initialQueries: DataQuery[];
queryTransactions: QueryTransaction[];
range: RawTimeRange;
scanning?: boolean;
scanRange?: RawTimeRange;
showingGraph: boolean;
showingLogs: boolean;
showingStartPage?: boolean;
+6
View File
@@ -267,6 +267,12 @@
}
}
.logs-nodata {
> * {
margin-left: 0.5em;
}
}
.logs-meta {
flex: 1;
color: $text-color-weak;
+2
View File
@@ -35,6 +35,8 @@ go run build.go -goarch arm64 -cc ${CCARM64} ${OPT} build
go run build.go -goos darwin -cc ${CCOSX64} ${OPT} build
go run build.go -goos windows -cc ${CCWIN64} ${OPT} build
# Do not remove CC from the linux build, its there for compatibility with Centos6
CC=${CCX64} go run build.go ${OPT} build
yarn install --pure-lockfile --no-progress