+3
-4
@@ -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 =
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 });
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -267,6 +267,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.logs-nodata {
|
||||
> * {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.logs-meta {
|
||||
flex: 1;
|
||||
color: $text-color-weak;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user