mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'metrics-tab-v3'
This commit is contained in:
@@ -209,7 +209,7 @@ func (hs *HttpServer) registerRoutes() {
|
||||
|
||||
r.Get("/plugins", wrap(GetPluginList))
|
||||
r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
|
||||
r.Get("/plugins/:pluginId/readme", wrap(GetPluginReadme))
|
||||
r.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown))
|
||||
|
||||
r.Group("/plugins", func() {
|
||||
r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards))
|
||||
|
||||
@@ -147,15 +147,16 @@ func GetPluginDashboards(c *middleware.Context) Response {
|
||||
}
|
||||
}
|
||||
|
||||
func GetPluginReadme(c *middleware.Context) Response {
|
||||
func GetPluginMarkdown(c *middleware.Context) Response {
|
||||
pluginId := c.Params(":pluginId")
|
||||
name := c.Params(":name")
|
||||
|
||||
if content, err := plugins.GetPluginReadme(pluginId); err != nil {
|
||||
if content, err := plugins.GetPluginMarkdown(pluginId, name); err != nil {
|
||||
if notfound, ok := err.(plugins.PluginNotFoundError); ok {
|
||||
return ApiError(404, notfound.Error(), nil)
|
||||
}
|
||||
|
||||
return ApiError(500, "Could not get readme", err)
|
||||
return ApiError(500, "Could not get markdown file", err)
|
||||
} else {
|
||||
return Respond(200, content)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package plugins
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type DataSourcePlugin struct {
|
||||
FrontendPluginBase
|
||||
Annotations bool `json:"annotations"`
|
||||
Metrics bool `json:"metrics"`
|
||||
Alerting bool `json:"alerting"`
|
||||
BuiltIn bool `json:"builtIn"`
|
||||
Mixed bool `json:"mixed"`
|
||||
Routes []*AppPluginRoute `json:"routes"`
|
||||
Annotations bool `json:"annotations"`
|
||||
Metrics bool `json:"metrics"`
|
||||
Alerting bool `json:"alerting"`
|
||||
QueryOptions map[string]bool `json:"queryOptions,omitempty"`
|
||||
BuiltIn bool `json:"builtIn,omitempty"`
|
||||
Mixed bool `json:"mixed,omitempty"`
|
||||
HasQueryHelp bool `json:"hasQueryHelp,omitempty"`
|
||||
|
||||
Routes []*AppPluginRoute `json:"-"`
|
||||
}
|
||||
|
||||
func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error {
|
||||
@@ -21,6 +28,15 @@ func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// look for help markdown
|
||||
helpPath := filepath.Join(p.PluginDir, "QUERY_HELP.md")
|
||||
if _, err := os.Stat(helpPath); os.IsNotExist(err) {
|
||||
helpPath = filepath.Join(p.PluginDir, "query_help.md")
|
||||
}
|
||||
if _, err := os.Stat(helpPath); err == nil {
|
||||
p.HasQueryHelp = true
|
||||
}
|
||||
|
||||
DataSources[p.Id] = p
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ type PluginBase struct {
|
||||
Includes []*PluginInclude `json:"includes"`
|
||||
Module string `json:"module"`
|
||||
BaseUrl string `json:"baseUrl"`
|
||||
HideFromList bool `json:"hideFromList"`
|
||||
State string `json:"state"`
|
||||
HideFromList bool `json:"hideFromList,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
|
||||
IncludedInAppId string `json:"-"`
|
||||
PluginDir string `json:"-"`
|
||||
@@ -48,9 +48,6 @@ type PluginBase struct {
|
||||
|
||||
GrafanaNetVersion string `json:"-"`
|
||||
GrafanaNetHasUpdate bool `json:"-"`
|
||||
|
||||
// cache for readme file contents
|
||||
Readme []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (pb *PluginBase) registerPlugin(pluginDir string) error {
|
||||
|
||||
@@ -3,6 +3,7 @@ package plugins
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
@@ -166,30 +167,24 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
||||
return loader.Load(jsonParser, currentDir)
|
||||
}
|
||||
|
||||
func GetPluginReadme(pluginId string) ([]byte, error) {
|
||||
func GetPluginMarkdown(pluginId string, name string) ([]byte, error) {
|
||||
plug, exists := Plugins[pluginId]
|
||||
if !exists {
|
||||
return nil, PluginNotFoundError{pluginId}
|
||||
}
|
||||
|
||||
if plug.Readme != nil {
|
||||
return plug.Readme, nil
|
||||
path := filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToUpper(name)))
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
path = filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToLower(name)))
|
||||
}
|
||||
|
||||
readmePath := filepath.Join(plug.PluginDir, "README.md")
|
||||
if _, err := os.Stat(readmePath); os.IsNotExist(err) {
|
||||
readmePath = filepath.Join(plug.PluginDir, "readme.md")
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return make([]byte, 0), nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(readmePath); os.IsNotExist(err) {
|
||||
plug.Readme = make([]byte, 0)
|
||||
return plug.Readme, nil
|
||||
}
|
||||
|
||||
if readmeBytes, err := ioutil.ReadFile(readmePath); err != nil {
|
||||
if data, err := ioutil.ReadFile(path); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
plug.Readme = readmeBytes
|
||||
return plug.Readme, nil
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,6 @@ function link(scope, elem, attrs) {
|
||||
enableSnippets: true
|
||||
});
|
||||
|
||||
console.log('getting completer', lang);
|
||||
if (scope.getCompleter()) {
|
||||
// make copy of array as ace seems to share completers array between instances
|
||||
codeEditor.completers = codeEditor.completers.slice();
|
||||
|
||||
@@ -20,6 +20,7 @@ import './jquery_extended';
|
||||
import './partials';
|
||||
import './components/jsontree/jsontree';
|
||||
import './components/code_editor/code_editor';
|
||||
import './utils/outline';
|
||||
|
||||
import {grafanaAppDirective} from './components/grafana_app';
|
||||
import {sideMenuDirective} from './components/sidemenu/sidemenu';
|
||||
|
||||
@@ -163,21 +163,15 @@ function($, _) {
|
||||
ms: 0.001
|
||||
};
|
||||
|
||||
kbn.calculateInterval = function(range, resolution, userInterval) {
|
||||
kbn.calculateInterval = function(range, resolution, lowLimitInterval) {
|
||||
var lowLimitMs = 1; // 1 millisecond default low limit
|
||||
var intervalMs, lowLimitInterval;
|
||||
var intervalMs;
|
||||
|
||||
if (userInterval) {
|
||||
if (userInterval[0] === '>') {
|
||||
lowLimitInterval = userInterval.slice(1);
|
||||
lowLimitMs = kbn.interval_to_ms(lowLimitInterval);
|
||||
}
|
||||
else {
|
||||
return {
|
||||
intervalMs: kbn.interval_to_ms(userInterval),
|
||||
interval: userInterval,
|
||||
};
|
||||
if (lowLimitInterval) {
|
||||
if (lowLimitInterval[0] === '>') {
|
||||
lowLimitInterval = lowLimitInterval.slice(1);
|
||||
}
|
||||
lowLimitMs = kbn.interval_to_ms(lowLimitInterval);
|
||||
}
|
||||
|
||||
intervalMs = kbn.round_interval((range.to.valueOf() - range.from.valueOf()) / resolution);
|
||||
|
||||
32
public/app/core/utils/outline.js
Normal file
32
public/app/core/utils/outline.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// outline.js
|
||||
// based on http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/
|
||||
(function(d) {
|
||||
"use strict";
|
||||
|
||||
var style_element = d.createElement('STYLE'),
|
||||
dom_events = 'addEventListener' in d,
|
||||
add_event_listener = function(type, callback) {
|
||||
// Basic cross-browser event handling
|
||||
if(dom_events){
|
||||
d.addEventListener(type, callback);
|
||||
} else {
|
||||
d.attachEvent('on' + type, callback);
|
||||
}
|
||||
},
|
||||
set_css = function(css_text) {
|
||||
// Handle setting of <style> element contents in IE8
|
||||
!!style_element.styleSheet ? style_element.styleSheet.cssText = css_text : style_element.innerHTML = css_text;
|
||||
};
|
||||
|
||||
d.getElementsByTagName('HEAD')[0].appendChild(style_element);
|
||||
|
||||
// Using mousedown instead of mouseover, so that previously focused elements don't lose focus ring on mouse move
|
||||
add_event_listener('mousedown', function() {
|
||||
set_css(':focus{outline:0 !important}::-moz-focus-inner{border:0;}');
|
||||
});
|
||||
|
||||
add_event_listener('keydown', function() {
|
||||
set_css('');
|
||||
});
|
||||
|
||||
})(document);
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
import {DashboardModel} from '../dashboard/model';
|
||||
import Remarkable from 'remarkable';
|
||||
|
||||
export class MetricsTabCtrl {
|
||||
dsName: string;
|
||||
@@ -13,9 +14,15 @@ export class MetricsTabCtrl {
|
||||
dashboard: DashboardModel;
|
||||
panelDsValue: any;
|
||||
addQueryDropdown: any;
|
||||
queryTroubleshooterOpen: boolean;
|
||||
helpOpen: boolean;
|
||||
optionsOpen: boolean;
|
||||
hasQueryHelp: boolean;
|
||||
helpHtml: string;
|
||||
queryOptions: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, private uiSegmentSrv, private datasourceSrv) {
|
||||
constructor($scope, private $sce, private datasourceSrv, private backendSrv, private $timeout) {
|
||||
this.panelCtrl = $scope.ctrl;
|
||||
$scope.ctrl = this;
|
||||
|
||||
@@ -33,6 +40,12 @@ export class MetricsTabCtrl {
|
||||
this.addQueryDropdown = {text: 'Add Query', value: null, fake: true};
|
||||
// update next ref id
|
||||
this.panelCtrl.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
|
||||
this.updateDatasourceOptions();
|
||||
}
|
||||
|
||||
updateDatasourceOptions() {
|
||||
this.hasQueryHelp = this.current.meta.hasQueryHelp;
|
||||
this.queryOptions = this.current.meta.queryOptions;
|
||||
}
|
||||
|
||||
getOptions(includeBuiltin) {
|
||||
@@ -50,6 +63,7 @@ export class MetricsTabCtrl {
|
||||
|
||||
this.current = option.datasource;
|
||||
this.panelCtrl.setDatasource(option.datasource);
|
||||
this.updateDatasourceOptions();
|
||||
}
|
||||
|
||||
addMixedQuery(option) {
|
||||
@@ -65,6 +79,29 @@ export class MetricsTabCtrl {
|
||||
addQuery() {
|
||||
this.panelCtrl.addQuery({isNew: true});
|
||||
}
|
||||
|
||||
toggleHelp() {
|
||||
this.optionsOpen = false;
|
||||
this.queryTroubleshooterOpen = false;
|
||||
this.helpOpen = !this.helpOpen;
|
||||
|
||||
this.backendSrv.get(`/api/plugins/${this.current.meta.id}/markdown/query_help`).then(res => {
|
||||
var md = new Remarkable();
|
||||
this.helpHtml = this.$sce.trustAsHtml(md.render(res));
|
||||
});
|
||||
}
|
||||
|
||||
toggleOptions() {
|
||||
this.helpOpen = false;
|
||||
this.queryTroubleshooterOpen = false;
|
||||
this.optionsOpen = !this.optionsOpen;
|
||||
}
|
||||
|
||||
toggleQueryTroubleshooter() {
|
||||
this.helpOpen = false;
|
||||
this.optionsOpen = false;
|
||||
this.queryTroubleshooterOpen = !this.queryTroubleshooterOpen;
|
||||
}
|
||||
}
|
||||
|
||||
/** @ngInject **/
|
||||
|
||||
@@ -29,7 +29,6 @@ export class PanelCtrl {
|
||||
fullscreen: boolean;
|
||||
inspector: any;
|
||||
editModeInitiated: boolean;
|
||||
editorHelpIndex: number;
|
||||
editMode: any;
|
||||
height: any;
|
||||
containerHeight: any;
|
||||
@@ -186,14 +185,6 @@ export class PanelCtrl {
|
||||
this.events.emit('render', payload);
|
||||
}
|
||||
|
||||
toggleEditorHelp(index) {
|
||||
if (this.editorHelpIndex === index) {
|
||||
this.editorHelpIndex = null;
|
||||
return;
|
||||
}
|
||||
this.editorHelpIndex = index;
|
||||
}
|
||||
|
||||
duplicate() {
|
||||
this.dashboard.duplicatePanel(this.panel, this.row);
|
||||
this.$timeout(() => {
|
||||
|
||||
@@ -1,9 +1,84 @@
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label gf-query-ds-label">
|
||||
<i class="icon-gf icon-gf-datasources"></i>
|
||||
</label>
|
||||
<label class="gf-form-label">Data Source</label>
|
||||
|
||||
<gf-form-dropdown model="ctrl.panelDsValue" css-class="gf-size-auto"
|
||||
lookup-text="true"
|
||||
get-options="ctrl.getOptions(true)"
|
||||
on-change="ctrl.datasourceChanged($option)">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label gf-form-label--grow"></label>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="ctrl.queryOptions">
|
||||
<a class="gf-form-label" ng-click="ctrl.toggleOptions()">
|
||||
<i class="fa fa-fw fa-caret-right" ng-hide="ctrl.optionsOpen"></i><i class="fa fa-fw fa-caret-down" ng-show="ctrl.optionsOpen"></i>Options
|
||||
</a>
|
||||
</div>
|
||||
<div class="gf-form" ng-if="ctrl.hasQueryHelp">
|
||||
<button class="gf-form-label" ng-click="ctrl.toggleHelp()">
|
||||
<i class="fa fa-fw fa-caret-right" ng-hide="ctrl.helpOpen"></i><i class="fa fa-fw fa-caret-down" ng-show="ctrl.helpOpen"></i>Help
|
||||
</button>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<button class="gf-form-label" ng-click="ctrl.toggleQueryTroubleshooter()" bs-tooltip="'Display query request & response'">
|
||||
<i class="fa fa-fw fa-caret-right" ng-hide="ctrl.queryTroubleshooterOpen"></i><i class="fa fa-fw fa-caret-down" ng-show="ctrl.queryTroubleshooterOpen"></i>Query Inspector
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div ng-if="ctrl.optionsOpen">
|
||||
<div class="gf-form gf-form--flex-end" ng-if="ctrl.queryOptions.minInterval">
|
||||
<label class="gf-form-label">Min time interval</label>
|
||||
<input type="text" class="gf-form-input width-6" placeholder="{{ctrl.panelCtrl.interval}}" ng-model="ctrl.panel.interval" spellcheck="false" ng-model-onblur ng-change="ctrl.panelCtrl.refresh()" />
|
||||
<info-popover mode="right-absolute">
|
||||
A lower limit for the auto group by time interval. Recommended to be set to write frequency,
|
||||
for example <code>1m</code> if your data is written every minute. Access auto interval via variable <code>$__interval</code> for time range
|
||||
string and <code>$__interval_ms</code> for numeric variable that can be used in math expressions.
|
||||
</info-popover>
|
||||
</div>
|
||||
<div class="gf-form gf-form--flex-end" ng-if="ctrl.queryOptions.cacheTimeout">
|
||||
<label class="gf-form-label width-9">Cache timeout</label>
|
||||
<input type="text" class="gf-form-input width-6" placeholder="60" ng-model="ctrl.panel.cacheTimeout" ng-model-onblur ng-change="ctrl.panelCtrl.refresh()" spellcheck="false" />
|
||||
<info-popover mode="right-absolute">
|
||||
If your time series store has a query cache this option can override the default
|
||||
cache timeout. Specify a numeric value in seconds.
|
||||
</info-popover>
|
||||
</div>
|
||||
<div class="gf-form gf-form--flex-end" ng-if="ctrl.queryOptions.maxDataPoints">
|
||||
<label class="gf-form-label width-9">Max data points</label>
|
||||
<input type="text" class="gf-form-input width-6" placeholder="auto" ng-model-onblur ng-change="ctrl.panelCtrl.refresh()" ng-model="ctrl.panel.maxDataPoints" spellcheck="false" />
|
||||
<info-popover mode="right-absolute">
|
||||
The maximum data points the query should return. For graphs this
|
||||
is automatically set to one data point per pixel.
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box" ng-if="ctrl.helpOpen">
|
||||
<div class="markdown-html" ng-bind-html="ctrl.helpHtml"></div>
|
||||
<a class="grafana-info-box__close" ng-click="ctrl.toggleHelp()">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<query-troubleshooter panel-ctrl="ctrl.panelCtrl" is-open="ctrl.queryTroubleshooterOpen"></query-troubleshooter>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="query-editor-rows gf-form-group">
|
||||
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
|
||||
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
|
||||
<plugin-component type="query-ctrl">
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
|
||||
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
|
||||
<plugin-component type="query-ctrl">
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-query">
|
||||
@@ -14,37 +89,14 @@
|
||||
</span>
|
||||
<span class="gf-form-query-letter-cell-letter">{{ctrl.panelCtrl.nextRefId}}</span>
|
||||
</label>
|
||||
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.current.meta.mixed">
|
||||
Add Query
|
||||
</button>
|
||||
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.current.meta.mixed">
|
||||
Add Query
|
||||
</button>
|
||||
|
||||
<div class="dropdown" ng-if="ctrl.current.meta.mixed">
|
||||
<gf-form-dropdown model="ctrl.addQueryDropdown"
|
||||
get-options="ctrl.getOptions(false)"
|
||||
on-change="ctrl.addMixedQuery($option)">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <query-troubleshooter panel-ctrl="ctrl.panelCtrl"></query-troubleshooter> -->
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">Panel Data Source</label>
|
||||
<gf-form-dropdown model="ctrl.panelDsValue"
|
||||
lookup-text="true"
|
||||
get-options="ctrl.getOptions(true)"
|
||||
on-change="ctrl.datasourceChanged($option)">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<rebuild-on-change property="ctrl.panel.datasource" show-null="true">
|
||||
<plugin-component type="query-options-ctrl">
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
<div class="dropdown" ng-if="ctrl.current.meta.mixed">
|
||||
<gf-form-dropdown model="ctrl.addQueryDropdown" get-options="ctrl.getOptions(false)" on-change="ctrl.addMixedQuery($option)">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,9 +5,8 @@ import appEvents from 'app/core/app_events';
|
||||
import {coreModule, JsonExplorer} from 'app/core/core';
|
||||
|
||||
const template = `
|
||||
<collapse-box title="Query Troubleshooter" is-open="ctrl.isOpen" state-changed="ctrl.stateChanged()"
|
||||
ng-class="{'collapse-box--error': ctrl.hasError}">
|
||||
<collapse-box-actions>
|
||||
<div class="query-troubleshooter" ng-if="ctrl.isOpen">
|
||||
<div class="query-troubleshooter__header">
|
||||
<a class="pointer" ng-click="ctrl.toggleExpand()" ng-hide="ctrl.allNodesExpanded">
|
||||
<i class="fa fa-plus-square-o"></i> Expand All
|
||||
</a>
|
||||
@@ -15,12 +14,12 @@ const template = `
|
||||
<i class="fa fa-minus-square-o"></i> Collapse All
|
||||
</a>
|
||||
<a class="pointer" clipboard-button="ctrl.getClipboardText()"><i class="fa fa-clipboard"></i> Copy to Clipboard</a>
|
||||
</collapse-box-actions>
|
||||
<collapse-box-body>
|
||||
</div>
|
||||
<div class="query-troubleshooter__body">
|
||||
<i class="fa fa-spinner fa-spin" ng-show="ctrl.isLoading"></i>
|
||||
<div class="query-troubleshooter-json"></div>
|
||||
</collapse-box-body>
|
||||
</collapse-box>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export class QueryTroubleshooterCtrl {
|
||||
@@ -42,7 +41,9 @@ export class QueryTroubleshooterCtrl {
|
||||
|
||||
appEvents.on('ds-request-response', this.onRequestResponseEventListener);
|
||||
appEvents.on('ds-request-error', this.onRequestErrorEventListener);
|
||||
|
||||
$scope.$on('$destroy', this.removeEventsListeners.bind(this));
|
||||
$scope.$watch('ctrl.isOpen', this.stateChanged.bind(this));
|
||||
}
|
||||
|
||||
removeEventsListeners() {
|
||||
@@ -51,6 +52,11 @@ export class QueryTroubleshooterCtrl {
|
||||
}
|
||||
|
||||
onRequestError(err) {
|
||||
// ignore if closed
|
||||
if (!this.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
this.hasError = true;
|
||||
this.onRequestResponse(err);
|
||||
@@ -133,7 +139,8 @@ export function queryTroubleshooter() {
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
panelCtrl: "="
|
||||
panelCtrl: "=",
|
||||
isOpen: "=",
|
||||
},
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import Remarkable from 'remarkable';
|
||||
|
||||
export class PluginEditCtrl {
|
||||
model: any;
|
||||
@@ -67,11 +68,9 @@ export class PluginEditCtrl {
|
||||
}
|
||||
|
||||
initReadme() {
|
||||
return this.backendSrv.get(`/api/plugins/${this.pluginId}/readme`).then(res => {
|
||||
return System.import('remarkable').then(Remarkable => {
|
||||
var md = new Remarkable();
|
||||
this.readmeHtml = this.$sce.trustAsHtml(md.render(res));
|
||||
});
|
||||
return this.backendSrv.get(`/api/plugins/${this.pluginId}/markdown/readme`).then(res => {
|
||||
var md = new Remarkable();
|
||||
this.readmeHtml = this.$sce.trustAsHtml(md.render(res));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export class IntervalVariable implements Variable {
|
||||
this.options.unshift({ text: 'auto', value: '$__auto_interval' });
|
||||
}
|
||||
|
||||
var res = kbn.calculateInterval(this.timeSrv.timeRange(), this.auto_count, (this.auto_min ? ">"+this.auto_min : null));
|
||||
var res = kbn.calculateInterval(this.timeSrv.timeRange(), this.auto_count, this.auto_min);
|
||||
this.templateSrv.setGrafanaVariable('$__auto_interval', res.interval);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,14 @@
|
||||
<span class="gf-form-label width-9">Version</span>
|
||||
<select class="gf-form-input gf-size-auto" ng-model="ctrl.current.jsonData.esVersion" ng-options="f.value as f.name for f in ctrl.esVersions"></select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h3 class="page-heading">Default query settings</h3>
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Group by time interval</span>
|
||||
<input class="gf-form-input max-width-9" type="text" ng-model="ctrl.current.jsonData.timeInterval" spellcheck='false' placeholder="example: >10s">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-9">Min interval</span>
|
||||
<input type="text" class="gf-form-input width-6" ng-model="ctrl.current.jsonData.timeInterval" spellcheck='false' placeholder="10s"></input>
|
||||
<info-popover mode="right-absolute">
|
||||
A lower limit for the auto group by time interval. Recommended to be set to write frequency,
|
||||
for example <code>1m</code> if your data is written every minute.
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<section class="grafana-metric-options">
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</span>
|
||||
<span class="gf-form-label">Group by time interval</span>
|
||||
|
||||
<input type="text" class="gf-form-input max-width-10" ng-model="ctrl.panelCtrl.panel.interval" ng-blur="ctrl.panelCtrl.refresh();"
|
||||
spellcheck='false' placeholder="example: >10s">
|
||||
|
||||
<span class="gf-form-label">
|
||||
<i class="fa fa-question-circle" bs-tooltip="'Set a low limit by having a greater sign: example: >60s'" data-placement="right"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</span>
|
||||
<span class="gf-form-label width-23">
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
alias patterns
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="pull-left">
|
||||
<div class="grafana-info-box" style="border: 0;" ng-if="ctrl.panelCtrl.editorHelpIndex === 1">
|
||||
<h5>Alias patterns</h5>
|
||||
<ul ng-non-bindable>
|
||||
<li>{{term fieldname}} = replaced with value of term group by</li>
|
||||
<li>{{metric}} = replaced with metric name (ex. Average, Min, Max)</li>
|
||||
<li>{{field}} = replaced with the metric field name</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,5 +21,9 @@
|
||||
},
|
||||
|
||||
"annotations": true,
|
||||
"metrics": true
|
||||
"metrics": true,
|
||||
|
||||
"queryOptions": {
|
||||
"minInterval": true
|
||||
}
|
||||
}
|
||||
|
||||
10
public/app/plugins/datasource/elasticsearch/query_help.md
Normal file
10
public/app/plugins/datasource/elasticsearch/query_help.md
Normal file
@@ -0,0 +1,10 @@
|
||||
#### Alias patterns
|
||||
- {{term fieldname}} = replaced with value of term group by
|
||||
- {{metric}} = replaced with metric name (ex. Average, Min, Max)
|
||||
- {{field}} = replaced with the metric field name
|
||||
|
||||
#### Documentation links
|
||||
|
||||
[Grafana's Elasticsearch Documentation](http://docs.grafana.org/features/datasources/elasticsearch)
|
||||
|
||||
[Official Elasticsearch Documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html)
|
||||
@@ -16,6 +16,19 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
|
||||
this.withCredentials = instanceSettings.withCredentials;
|
||||
this.render_method = instanceSettings.render_method || 'POST';
|
||||
|
||||
this.getQueryOptionsInfo = function() {
|
||||
return {
|
||||
"maxDataPoints": true,
|
||||
"cacheTimeout": true,
|
||||
"links": [
|
||||
{
|
||||
text: "Help",
|
||||
url: "http://docs.grafana.org/features/datasources/graphite/#using-graphite-in-grafana"
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
this.query = function(options) {
|
||||
var graphOptions = {
|
||||
from: this.translateTime(options.rangeRaw.from, false),
|
||||
|
||||
@@ -2,10 +2,6 @@ import {GraphiteDatasource} from './datasource';
|
||||
import {GraphiteQueryCtrl} from './query_ctrl';
|
||||
import {GraphiteConfigCtrl} from './config_ctrl';
|
||||
|
||||
class GraphiteQueryOptionsCtrl {
|
||||
static templateUrl = 'partials/query.options.html';
|
||||
}
|
||||
|
||||
class AnnotationsQueryCtrl {
|
||||
static templateUrl = 'partials/annotations.editor.html';
|
||||
}
|
||||
@@ -14,7 +10,6 @@ export {
|
||||
GraphiteDatasource as Datasource,
|
||||
GraphiteQueryCtrl as QueryCtrl,
|
||||
GraphiteConfigCtrl as ConfigCtrl,
|
||||
GraphiteQueryOptionsCtrl as QueryOptionsCtrl,
|
||||
AnnotationsQueryCtrl as AnnotationsQueryCtrl,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
<section class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-15">
|
||||
<span class="gf-form-label width-8">
|
||||
Cache timeout
|
||||
</span>
|
||||
<input type="text"
|
||||
class="gf-form-input"
|
||||
ng-model="ctrl.panelCtrl.panel.cacheTimeout"
|
||||
bs-tooltip="'Graphite parameter to override memcache default timeout (unit is seconds)'"
|
||||
data-placement="right"
|
||||
spellcheck='false'
|
||||
placeholder="60">
|
||||
</input>
|
||||
</div>
|
||||
<div class="gf-form max-width-15">
|
||||
<span class="gf-form-label">Max data points</span>
|
||||
<input type="text"
|
||||
class="gf-form-input"
|
||||
ng-model="ctrl.panelCtrl.panel.maxDataPoints"
|
||||
bs-tooltip="'Override max data points, automatically set to graph width in pixels.'"
|
||||
data-placement="right"
|
||||
ng-model-onblur ng-change="ctrl.panelCtrl.refresh()"
|
||||
spellcheck='false'
|
||||
placeholder="auto">
|
||||
</input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-12">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
Shorter legend names
|
||||
</a>
|
||||
</span>
|
||||
<span class="gf-form-label width-12">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(2);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
Series as parameters
|
||||
</a>
|
||||
</span>
|
||||
<span class="gf-form-label width-7">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
Stacking
|
||||
</a>
|
||||
</span>
|
||||
<span class="gf-form-label width-8">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(4)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
Templating
|
||||
</a>
|
||||
</span>
|
||||
<span class="gf-form-label width-10">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(5)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
max data points
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="grafana-info-box span8" ng-if="ctrl.panelCtrl.editorHelpIndex === 1">
|
||||
<h5>Shorter legend names</h5>
|
||||
<ul>
|
||||
<li>alias() function to specify a custom series name</li>
|
||||
<li>aliasByNode(2) to alias by a specific part of your metric path</li>
|
||||
<li>aliasByNode(2, -1) you can add multiple segment paths, and use negative index</li>
|
||||
<li>groupByNode(2, 'sum') is useful if you have 2 wildcards in your metric path and want to sumSeries and group by</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span8" ng-if="ctrl.panelCtrl.editorHelpIndex === 2">
|
||||
<h5>Series as parameter</h5>
|
||||
<ul>
|
||||
<li>Some graphite functions allow you to have many series arguments</li>
|
||||
<li>Use #[A-Z] to use a graphite query as parameter to a function</li>
|
||||
<li>
|
||||
Examples:
|
||||
<ul>
|
||||
<li>asPercent(#A, #B)</li>
|
||||
<li>prod.srv-01.counters.count - asPercent(#A) : percentage of count in comparison with A query</li>
|
||||
<li>prod.srv-01.counters.count - sumSeries(#A) : sum count and series A </li>
|
||||
<li>divideSeries(#A, #B)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>If a query is added only to be used as a parameter, hide it from the graph with the eye icon</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 3">
|
||||
<h5>Stacking</h5>
|
||||
<ul>
|
||||
<li>You find the stacking option under Display Styles tab</li>
|
||||
<li>When stacking is enabled make sure null point mode is set to 'null as zero'</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 4">
|
||||
<h5>Templating</h5>
|
||||
<ul>
|
||||
<li>You can use a template variable in place of metric names</li>
|
||||
<li>You can use a template variable in place of function parameters</li>
|
||||
<li>You enable the templating feature in Dashboard settings / Feature toggles </li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 5">
|
||||
<h5>Max data points</h5>
|
||||
<ul>
|
||||
<li>Every graphite request is issued with a maxDataPoints parameter</li>
|
||||
<li>Graphite uses this parameter to consolidate the real number of values down to this number</li>
|
||||
<li>If there are more real values, then by default they will be consolidated using averages</li>
|
||||
<li>This could hide real peaks and max values in your series</li>
|
||||
<li>You can change how point consolidation is made using the consolidateBy graphite function</li>
|
||||
<li>Point consolidation will effect series legend values (min,max,total,current)</li>
|
||||
<li>If you override maxDataPoint and set a high value performance can be severely effected</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11,6 +11,11 @@
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
|
||||
"queryOptions": {
|
||||
"maxDataPoints": true,
|
||||
"cacheTimeout": true
|
||||
},
|
||||
|
||||
"info": {
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
|
||||
30
public/app/plugins/datasource/graphite/query_help.md
Normal file
30
public/app/plugins/datasource/graphite/query_help.md
Normal file
@@ -0,0 +1,30 @@
|
||||
#### Get Shorter legend names
|
||||
|
||||
- alias() function to specify a custom series name
|
||||
- aliasByNode(2) to alias by a specific part of your metric path
|
||||
- groupByNode(2, 'sum') is useful if you have 2 wildcards in your metric path and want to sumSeries and group by.
|
||||
|
||||
#### Series as parameter
|
||||
|
||||
- Some graphite functions allow you to have many series arguments
|
||||
- Use #[A-Z] to use a graphite query as parameter to a function
|
||||
- Examples:
|
||||
- asPercent(#A, #B)
|
||||
- divideSeries(#A, #B)
|
||||
|
||||
If a query is added only to be used as a parameter, hide it from the graph with the eye icon
|
||||
|
||||
#### Max data points
|
||||
- Every graphite request is issued with a maxDataPoints parameter
|
||||
- Graphite uses this parameter to consolidate the real number of values down to this number
|
||||
- If there are more real values, then by default they will be consolidated using averages
|
||||
- This could hide real peaks and max values in your series
|
||||
- You can change how point consolidation is made using the consolidateBy graphite function
|
||||
- Point consolidation will effect series legend values (min,max,total,current)
|
||||
- if you override maxDataPoint and set a high value performance can be severely effected
|
||||
|
||||
#### Documentation links:
|
||||
|
||||
[Grafana's Graphite Documentation](http://docs.grafana.org/features/datasources/graphite)
|
||||
|
||||
[Official Graphite Documentation](https://graphite.readthedocs.io)
|
||||
@@ -5,10 +5,6 @@ class InfluxConfigCtrl {
|
||||
static templateUrl = 'partials/config.html';
|
||||
}
|
||||
|
||||
class InfluxQueryOptionsCtrl {
|
||||
static templateUrl = 'partials/query.options.html';
|
||||
}
|
||||
|
||||
class InfluxAnnotationsQueryCtrl {
|
||||
static templateUrl = 'partials/annotations.editor.html';
|
||||
}
|
||||
@@ -17,7 +13,6 @@ export {
|
||||
InfluxDatasource as Datasource,
|
||||
InfluxQueryCtrl as QueryCtrl,
|
||||
InfluxConfigCtrl as ConfigCtrl,
|
||||
InfluxQueryOptionsCtrl as QueryOptionsCtrl,
|
||||
InfluxAnnotationsQueryCtrl as AnnotationsQueryCtrl,
|
||||
};
|
||||
|
||||
|
||||
@@ -24,10 +24,14 @@
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form max-width-21">
|
||||
<span class="gf-form-label">Default group by time</span>
|
||||
<input type="text" class="gf-form-input width-6" ng-model="ctrl.current.jsonData.timeInterval"
|
||||
spellcheck='false' placeholder="example: >10s"></input>
|
||||
<i class="fa fa-question-circle" bs-tooltip="'Set a low limit by having a greater sign: example: >10s'" data-placement="right"></i>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Min time interval</span>
|
||||
<input type="text" class="gf-form-input width-6" ng-model="ctrl.current.jsonData.timeInterval" spellcheck='false' placeholder="10s"></input>
|
||||
<info-popover mode="right-absolute">
|
||||
A lower limit for the auto group by time interval. Recommended to be set to write frequency,
|
||||
for example <code>1m</code> if your data is written every minute.
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
<section class="grafana-metric-options">
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label"><i class="fa fa-wrench"></i></span>
|
||||
<span class="gf-form-label width-11">Group by time interval</span>
|
||||
<input type="text" class="gf-form-input width-16" ng-model="ctrl.panelCtrl.panel.interval" ng-blur="ctrl.panelCtrl.refresh();"
|
||||
spellcheck='false' placeholder="example: >10s">
|
||||
<info-popover mode="right-absolute">
|
||||
Set a low limit by having a greater sign: example: >60s
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
alias patterns
|
||||
</a>
|
||||
</span>
|
||||
<span class="gf-form-label width-10">
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(2)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
stacking & fill
|
||||
</a>
|
||||
</span>
|
||||
<span class="gf-form-label width-10">
|
||||
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
group by time
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 1">
|
||||
<h5>Alias patterns</h5>
|
||||
<ul>
|
||||
<li>$m = replaced with measurement name</li>
|
||||
<li>$measurement = replaced with measurement name</li>
|
||||
<li>$1 - $9 = replaced with part of measurement name (if you separate your measurement name with dots)</li>
|
||||
<li>$col = replaced with column name</li>
|
||||
<li>$tag_exampletag = replaced with the value of the <i>exampletag</i> tag</li>
|
||||
<li>You can also use [[tag_exampletag]] pattern replacement syntax</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 2">
|
||||
<h5>Stacking and fill</h5>
|
||||
<ul>
|
||||
<li>When stacking is enabled it is important that points align</li>
|
||||
<li>If there are missing points for one series it can cause gaps or missing bars</li>
|
||||
<li>You must use fill(0), and select a group by time low limit</li>
|
||||
<li>Use the group by time option below your queries and specify for example >10s if your metrics are written every 10 seconds</li>
|
||||
<li>This will insert zeros for series that are missing measurements and will make stacking work properly</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 3">
|
||||
<h5>Group by time</h5>
|
||||
<ul>
|
||||
<li>Group by time is important, otherwise the query could return many thousands of datapoints that will slow down Grafana</li>
|
||||
<li>Leave the group by time field empty for each query and it will be calculated based on time range and pixel width of the graph</li>
|
||||
<li>If you use fill(0) or fill(null) set a low limit for the auto group by time interval</li>
|
||||
<li>The low limit can only be set in the group by time option below your queries</li>
|
||||
<li>You set a low limit by adding a greater sign before the interval</li>
|
||||
<li>Example: >60s if you write metrics to InfluxDB every 60 seconds</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
"annotations": true,
|
||||
"alerting": true,
|
||||
|
||||
"queryOptions": {
|
||||
"minInterval": true
|
||||
},
|
||||
|
||||
"info": {
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
|
||||
28
public/app/plugins/datasource/influxdb/query_help.md
Normal file
28
public/app/plugins/datasource/influxdb/query_help.md
Normal file
@@ -0,0 +1,28 @@
|
||||
#### Alias patterns
|
||||
- replaced with measurement name
|
||||
- $measurement = replaced with measurement name
|
||||
- $1 - $9 = replaced with part of measurement name (if you separate your measurement name with dots)
|
||||
- $col = replaced with column name
|
||||
- $tag_exampletag = replaced with the value of the <i>exampletag</i> tag
|
||||
- You can also use [[tag_exampletag]] pattern replacement syntax
|
||||
|
||||
#### Stacking and fill
|
||||
- When stacking is enabled it is important that points align
|
||||
- If there are missing points for one series it can cause gaps or missing bars
|
||||
- You must use fill(0), and select a group by time low limit
|
||||
- Use the group by time option below your queries and specify for example >10s if your metrics are written every 10 seconds
|
||||
- This will insert zeros for series that are missing measurements and will make stacking work properly
|
||||
|
||||
#### Group by time
|
||||
- Group by time is important, otherwise the query could return many thousands of datapoints that will slow down Grafana
|
||||
- Leave the group by time field empty for each query and it will be calculated based on time range and pixel width of the graph
|
||||
- If you use fill(0) or fill(null) set a low limit for the auto group by time interval
|
||||
- The low limit can only be set in the group by time option below your queries
|
||||
- You set a low limit by adding a greater sign before the interval
|
||||
- Example: >60s if you write metrics to InfluxDB every 60 seconds
|
||||
|
||||
#### Documentation links:
|
||||
|
||||
[Grafana's InfluxDB Documentation](http://docs.grafana.org/features/datasources/influxdb)
|
||||
|
||||
[Official InfluxDB Documentation](https://docs.influxdata.com/influxdb)
|
||||
@@ -276,7 +276,7 @@ $card-background-hover: linear-gradient(135deg, #343434, #262626);
|
||||
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3);
|
||||
|
||||
// info box
|
||||
$info-box-background: linear-gradient(120deg, #142749, #0e203e);
|
||||
$info-box-background: linear-gradient(100deg, #1a4552, #0b2127);
|
||||
|
||||
// footer
|
||||
$footer-link-color: $gray-1;
|
||||
|
||||
@@ -16,6 +16,10 @@ $gf-form-margin: 0.25rem;
|
||||
&--grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&--flex-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-disabled {
|
||||
@@ -54,7 +58,6 @@ $gf-form-margin: 0.25rem;
|
||||
background-color: $input-label-bg;
|
||||
display: block;
|
||||
font-size: $font-size-sm;
|
||||
margin-right: $gf-form-margin;
|
||||
|
||||
border: $input-btn-border-width solid transparent;
|
||||
@include border-radius($label-border-radius-sm);
|
||||
@@ -103,7 +106,6 @@ $gf-form-margin: 0.25rem;
|
||||
padding: $input-padding-y $input-padding-x;
|
||||
margin-right: $gf-form-margin;
|
||||
font-size: $font-size-base;
|
||||
margin-right: $gf-form-margin;
|
||||
line-height: $input-line-height;
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
@@ -112,7 +114,6 @@ $gf-form-margin: 0.25rem;
|
||||
border: $input-btn-border-width solid $input-border-color;
|
||||
@include border-radius($input-border-radius-sm);
|
||||
@include box-shadow($input-box-shadow);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -235,7 +236,6 @@ $gf-form-margin: 0.25rem;
|
||||
font-size: $font-size-sm;
|
||||
box-shadow: none;
|
||||
border: $input-btn-border-width solid transparent;
|
||||
@include border-radius($label-border-radius-sm);
|
||||
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
@@ -281,7 +281,7 @@ $gf-form-margin: 0.25rem;
|
||||
&--right-absolute {
|
||||
position: absolute;
|
||||
right: $spacer;
|
||||
top: 8px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
&--right-normal {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
.grafana-info-box::before {
|
||||
content: "\f05a";
|
||||
font-family:'FontAwesome';
|
||||
position: absolute;
|
||||
top: -13px;
|
||||
left: -8px;
|
||||
font-size: 20px;
|
||||
color: $text-color;
|
||||
}
|
||||
// .grafana-info-box::before {
|
||||
// content: "\f05a";
|
||||
// font-family:'FontAwesome';
|
||||
// position: absolute;
|
||||
// top: -13px;
|
||||
// left: -8px;
|
||||
// font-size: 20px;
|
||||
// color: $text-color;
|
||||
// }
|
||||
|
||||
.grafana-info-box {
|
||||
position: relative;
|
||||
@@ -15,12 +15,14 @@
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: $spacer;
|
||||
margin-right: $gf-form-margin;
|
||||
flex-grow: 1;
|
||||
|
||||
h5 {
|
||||
margin-bottom: $spacer;
|
||||
}
|
||||
ul {
|
||||
padding-left: $spacer;
|
||||
padding-left: $spacer * 1.5;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -28,3 +30,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.grafana-info-box__close {
|
||||
text-align: center;
|
||||
display: block;
|
||||
color: $link-color !important;
|
||||
height: 0;
|
||||
position: relative;
|
||||
top: -9px;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.gf-query-ds-label {
|
||||
text-align: center;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.grafana-metric-options {
|
||||
margin-top: 25px;
|
||||
}
|
||||
@@ -146,3 +151,25 @@ input[type="text"].tight-form-func-param {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.query-troubleshooter {
|
||||
font-size: $font-size-sm;
|
||||
margin: $gf-form-margin;
|
||||
border: 1px solid $btn-secondary-bg;
|
||||
min-height: 100px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.query-troubleshooter__header {
|
||||
float: right;
|
||||
font-size: $font-size-sm;
|
||||
text-align: right;
|
||||
padding: $input-padding-y $input-padding-x;
|
||||
a {
|
||||
margin-left: $spacer;
|
||||
}
|
||||
}
|
||||
|
||||
.query-troubleshooter__body {
|
||||
padding: $spacer 0;
|
||||
}
|
||||
|
||||
@@ -23,3 +23,14 @@
|
||||
-o-animation: #{$str};
|
||||
animation: #{$str};
|
||||
}
|
||||
|
||||
.animate-height {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&--open {
|
||||
max-height: 1000px;
|
||||
overflow: auto;
|
||||
transition: max-height 250ms ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +143,8 @@ define([
|
||||
expect(res.intervalMs).to.be(500);
|
||||
});
|
||||
|
||||
it('fixed user interval', function() {
|
||||
var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
|
||||
it('fixed user min interval', function() {
|
||||
var range = {from: dateMath.parse('now-10m'), to: dateMath.parse('now')};
|
||||
var res = kbn.calculateInterval(range, 1600, '10s');
|
||||
expect(res.interval).to.be('10s');
|
||||
expect(res.intervalMs).to.be(10000);
|
||||
|
||||
Reference in New Issue
Block a user