diff --git a/conf/defaults.ini b/conf/defaults.ini index 306c625d980..a9aa2239b16 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -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 = - diff --git a/conf/sample.ini b/conf/sample.ini index c6b716a731d..3c61b2b61d1 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -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 diff --git a/docs/sources/reference/export_import.md b/docs/sources/reference/export_import.md index 31f32d890f6..501e9aca62b 100644 --- a/docs/sources/reference/export_import.md +++ b/docs/sources/reference/export_import.md @@ -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. diff --git a/public/app/core/utils/kbn.ts b/public/app/core/utils/kbn.ts index 5d417d24169..9caa5bf1a54 100644 --- a/public/app/core/utils/kbn.ts +++ b/public/app/core/utils/kbn.ts @@ -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 }); }; diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index d4e9b689495..44380877c34 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -97,6 +97,11 @@ export class Explore extends React.PureComponent { * Local ID cache to compare requested vs selected datasource */ requestedDatasourceId: string; + scanTimer: NodeJS.Timer; + /** + * Timepicker to control scanning + */ + timepickerRef: React.RefObject; constructor(props) { super(props); @@ -122,6 +127,7 @@ export class Explore extends React.PureComponent { history: [], queryTransactions: [], range: initialRange, + scanning: false, showingGraph: true, showingLogs: true, showingStartPage: false, @@ -132,6 +138,7 @@ export class Explore extends React.PureComponent { }; } this.modifiedQueries = initialQueries.slice(); + this.timepickerRef = React.createRef(); } async componentDidMount() { @@ -164,6 +171,10 @@ export class Explore extends React.PureComponent { } } + 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 { } }; - 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 { ); }; + 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 { 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 { } 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 { 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 { initialQueries, queryTransactions, range, + scanning, + scanRange, showingGraph, showingLogs, showingStartPage, @@ -822,7 +865,7 @@ export class Explore extends React.PureComponent { ) : null} - +
); } diff --git a/public/app/features/explore/TimePicker.tsx b/public/app/features/explore/TimePicker.tsx index a3578263cea..47c52b07292 100644 --- a/public/app/features/explore/TimePicker.tsx +++ b/public/app/features/explore/TimePicker.tsx @@ -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 { - onChangeTime(nextRange); + onChangeTime(nextRange, scanning); } ); + + return nextRange; } handleChangeFrom = e => { diff --git a/public/app/plugins/datasource/elasticsearch/bucket_agg.ts b/public/app/plugins/datasource/elasticsearch/bucket_agg.ts index cacf86201fe..8701b2bf335 100644 --- a/public/app/plugins/datasource/elasticsearch/bucket_agg.ts +++ b/public/app/plugins/datasource/elasticsearch/bucket_agg.ts @@ -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); diff --git a/public/app/plugins/datasource/elasticsearch/metric_agg.ts b/public/app/plugins/datasource/elasticsearch/metric_agg.ts index 56f874d90b9..cae5be45720 100644 --- a/public/app/plugins/datasource/elasticsearch/metric_agg.ts +++ b/public/app/plugins/datasource/elasticsearch/metric_agg.ts @@ -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); diff --git a/public/app/plugins/datasource/logging/datasource.test.ts b/public/app/plugins/datasource/logging/datasource.test.ts index 212d352dfca..2e2adb144ef 100644 --- a/public/app/plugins/datasource/logging/datasource.test.ts +++ b/public/app/plugins/datasource/logging/datasource.test.ts @@ -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: '', + }); + }); }); diff --git a/public/app/plugins/datasource/logging/datasource.ts b/public/app/plugins/datasource/logging/datasource.ts index 494dcd78d6c..e36da73cd66 100644 --- a/public/app/plugins/datasource/logging/datasource.ts +++ b/public/app/plugins/datasource/logging/datasource.ts @@ -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 }; diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index f80a485fc29..d9ace7b74c0 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -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; diff --git a/public/sass/pages/_explore.scss b/public/sass/pages/_explore.scss index 23c6fbf0916..5c2848d018b 100644 --- a/public/sass/pages/_explore.scss +++ b/public/sass/pages/_explore.scss @@ -267,6 +267,12 @@ } } + .logs-nodata { + > * { + margin-left: 0.5em; + } + } + .logs-meta { flex: 1; color: $text-color-weak; diff --git a/scripts/build/build-all.sh b/scripts/build/build-all.sh index be0b297527b..3013452a279 100755 --- a/scripts/build/build-all.sh +++ b/scripts/build/build-all.sh @@ -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