Просмотр исходного кода

Merge pull request #1 from grafana/master

Update local master
Pavel 7 лет назад
Родитель
Сommit
01c2a43699

+ 3 - 4
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 =
-

+ 3 - 0
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

+ 2 - 0
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.

+ 6 - 0
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 });
 };
 

+ 51 - 4
public/app/features/explore/Explore.tsx

@@ -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
public/app/features/explore/Logs.tsx

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

+ 15 - 16
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);

+ 16 - 16
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);

+ 7 - 0
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: '',
+    });
+  });
 });

+ 5 - 16
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 };

+ 3 - 0
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;

+ 6 - 0
public/sass/pages/_explore.scss

@@ -267,6 +267,12 @@
       }
     }
 
+    .logs-nodata {
+      > * {
+        margin-left: 0.5em;
+      }
+    }
+
     .logs-meta {
       flex: 1;
       color: $text-color-weak;

+ 2 - 0
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