Prometheus: Refactoring in datasource.ts (#107208)
* start cleanup * update vars * prettier
This commit is contained in:
@@ -6,28 +6,43 @@ export const PROMETHEUS_QUERY_BUILDER_MAX_RESULTS = 1000;
|
||||
export const PROM_CONFIG_LABEL_WIDTH = 30;
|
||||
|
||||
export const LIST_ITEM_SIZE = 25;
|
||||
|
||||
export const LAST_USED_LABELS_KEY = 'grafana.datasources.prometheus.browser.labels';
|
||||
|
||||
// single duration input
|
||||
export const DURATION_REGEX = /^$|^\d+(ms|[Mwdhmsy])$/;
|
||||
|
||||
// multiple duration input
|
||||
export const MULTIPLE_DURATION_REGEX = /(\d+)(.+)/;
|
||||
|
||||
export const NON_NEGATIVE_INTEGER_REGEX = /^(0|[1-9]\d*)(\.\d+)?(e\+?\d+)?$/; // non-negative integers, including scientific notation
|
||||
|
||||
export const EMPTY_SELECTOR = '{}';
|
||||
|
||||
export const DEFAULT_SERIES_LIMIT = 40000;
|
||||
|
||||
export const MATCH_ALL_LABELS_STR = '__name__!=""';
|
||||
|
||||
export const MATCH_ALL_LABELS = '{__name__!=""}';
|
||||
|
||||
export const METRIC_LABEL = '__name__';
|
||||
|
||||
export const durationError = 'Value is not valid, you can use number with time unit specifier: y, M, w, d, h, m, s';
|
||||
|
||||
export const countError = 'Value is not valid, you can use non-negative integers, including scientific notation';
|
||||
|
||||
export const seriesLimitError =
|
||||
'Value is not valid, you can use only numbers or leave it empty to use default limit or set 0 to have no limit.';
|
||||
|
||||
export const InstantQueryRefIdIndex = '-Instant';
|
||||
|
||||
export const GET_AND_POST_METADATA_ENDPOINTS = [
|
||||
'api/v1/query',
|
||||
'api/v1/query_range',
|
||||
'api/v1/series',
|
||||
'api/v1/labels',
|
||||
'suggestions',
|
||||
];
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const REMOVE_SERIES_LIMIT = 'none';
|
||||
|
||||
export const durationError = 'Value is not valid, you can use number with time unit specifier: y, M, w, d, h, m, s';
|
||||
export const countError = 'Value is not valid, you can use non-negative integers, including scientific notation';
|
||||
export const seriesLimitError =
|
||||
'Value is not valid, you can use only numbers or leave it empty to use default limit or set 0 to have no limit.';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/datasource.ts
|
||||
import { defaults } from 'lodash';
|
||||
import { tz } from 'moment-timezone';
|
||||
import { lastValueFrom, Observable, throwError } from 'rxjs';
|
||||
@@ -42,7 +41,12 @@ import {
|
||||
|
||||
import { addLabelToQuery } from './add_label_to_query';
|
||||
import { PrometheusAnnotationSupport } from './annotations';
|
||||
import { DEFAULT_SERIES_LIMIT, SUGGESTIONS_LIMIT } from './constants';
|
||||
import {
|
||||
DEFAULT_SERIES_LIMIT,
|
||||
GET_AND_POST_METADATA_ENDPOINTS,
|
||||
InstantQueryRefIdIndex,
|
||||
SUGGESTIONS_LIMIT,
|
||||
} from './constants';
|
||||
import { prometheusRegularEscape, prometheusSpecialRegexEscape } from './escaping';
|
||||
import {
|
||||
exportToAbstractQuery,
|
||||
@@ -72,44 +76,34 @@ import {
|
||||
import { utf8Support, wrapUtf8Filters } from './utf8_support';
|
||||
import { PrometheusVariableSupport } from './variables';
|
||||
|
||||
const GET_AND_POST_METADATA_ENDPOINTS = [
|
||||
'api/v1/query',
|
||||
'api/v1/query_range',
|
||||
'api/v1/series',
|
||||
'api/v1/labels',
|
||||
'suggestions',
|
||||
];
|
||||
|
||||
export const InstantQueryRefIdIndex = '-Instant';
|
||||
|
||||
export class PrometheusDatasource
|
||||
extends DataSourceWithBackend<PromQuery, PromOptions>
|
||||
implements DataSourceWithQueryImportSupport<PromQuery>, DataSourceWithQueryExportSupport<PromQuery>
|
||||
{
|
||||
type: string;
|
||||
ruleMappings: RuleQueryMapping;
|
||||
hasIncrementalQuery: boolean;
|
||||
url: string;
|
||||
id: number;
|
||||
access: 'direct' | 'proxy';
|
||||
basicAuth: any;
|
||||
withCredentials: boolean;
|
||||
interval: string;
|
||||
httpMethod: string;
|
||||
languageProvider: PrometheusLanguageProviderInterface;
|
||||
exemplarTraceIdDestinations: ExemplarTraceIdDestination[] | undefined;
|
||||
lookupsDisabled: boolean;
|
||||
cache: QueryCache<PromQuery>;
|
||||
cacheLevel: PrometheusCacheLevel;
|
||||
customQueryParameters: URLSearchParams;
|
||||
datasourceConfigurationPrometheusFlavor?: PromApplication;
|
||||
datasourceConfigurationPrometheusVersion?: string;
|
||||
disableRecordingRules: boolean;
|
||||
defaultEditor?: QueryEditorMode;
|
||||
exemplarTraceIdDestinations: ExemplarTraceIdDestination[] | undefined;
|
||||
exemplarsAvailable: boolean;
|
||||
cacheLevel: PrometheusCacheLevel;
|
||||
cache: QueryCache<PromQuery>;
|
||||
hasIncrementalQuery: boolean;
|
||||
httpMethod: string;
|
||||
id: number;
|
||||
interval: string;
|
||||
languageProvider: PrometheusLanguageProviderInterface;
|
||||
lookupsDisabled: boolean;
|
||||
metricNamesAutocompleteSuggestionLimit: number;
|
||||
ruleMappings: RuleQueryMapping;
|
||||
seriesEndpoint: boolean;
|
||||
seriesLimit: number;
|
||||
type: string;
|
||||
url: string;
|
||||
withCredentials: boolean;
|
||||
defaultEditor?: QueryEditorMode;
|
||||
|
||||
constructor(
|
||||
instanceSettings: DataSourceInstanceSettings<PromOptions>,
|
||||
@@ -118,48 +112,106 @@ export class PrometheusDatasource
|
||||
) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.type = 'prometheus';
|
||||
this.id = instanceSettings.id;
|
||||
this.url = instanceSettings.url!;
|
||||
// DATASOURCE CONFIGURATION PROPERTIES
|
||||
this.access = instanceSettings.access;
|
||||
this.basicAuth = instanceSettings.basicAuth;
|
||||
this.withCredentials = Boolean(instanceSettings.withCredentials);
|
||||
this.interval = instanceSettings.jsonData.timeInterval || '15s';
|
||||
this.httpMethod = instanceSettings.jsonData.httpMethod || 'GET';
|
||||
this.exemplarTraceIdDestinations = instanceSettings.jsonData.exemplarTraceIdDestinations;
|
||||
this.hasIncrementalQuery = instanceSettings.jsonData.incrementalQuerying ?? false;
|
||||
this.ruleMappings = {};
|
||||
this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup ?? false;
|
||||
this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters);
|
||||
this.datasourceConfigurationPrometheusFlavor = instanceSettings.jsonData.prometheusType;
|
||||
this.datasourceConfigurationPrometheusVersion = instanceSettings.jsonData.prometheusVersion;
|
||||
this.seriesLimit = instanceSettings.jsonData.seriesLimit ?? DEFAULT_SERIES_LIMIT;
|
||||
this.seriesEndpoint = instanceSettings.jsonData.seriesEndpoint ?? false;
|
||||
this.defaultEditor = instanceSettings.jsonData.defaultEditor;
|
||||
this.disableRecordingRules = instanceSettings.jsonData.disableRecordingRules ?? false;
|
||||
this.variables = new PrometheusVariableSupport(this, this.templateSrv);
|
||||
this.exemplarsAvailable = true;
|
||||
this.cacheLevel = instanceSettings.jsonData.cacheLevel ?? PrometheusCacheLevel.Low;
|
||||
this.metricNamesAutocompleteSuggestionLimit =
|
||||
instanceSettings.jsonData.codeModeMetricNamesSuggestionLimit ?? SUGGESTIONS_LIMIT;
|
||||
|
||||
this.cache = new QueryCache({
|
||||
getTargetSignature: this.getPrometheusTargetSignature.bind(this),
|
||||
overlapString: instanceSettings.jsonData.incrementalQueryOverlapWindow ?? defaultPrometheusQueryOverlapWindow,
|
||||
applyInterpolation: this.interpolateString.bind(this),
|
||||
});
|
||||
this.cacheLevel = instanceSettings.jsonData.cacheLevel ?? PrometheusCacheLevel.Low;
|
||||
this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters);
|
||||
this.datasourceConfigurationPrometheusFlavor = instanceSettings.jsonData.prometheusType;
|
||||
this.datasourceConfigurationPrometheusVersion = instanceSettings.jsonData.prometheusVersion;
|
||||
this.disableRecordingRules = instanceSettings.jsonData.disableRecordingRules ?? false;
|
||||
this.exemplarTraceIdDestinations = instanceSettings.jsonData.exemplarTraceIdDestinations;
|
||||
this.exemplarsAvailable = true;
|
||||
this.hasIncrementalQuery = instanceSettings.jsonData.incrementalQuerying ?? false;
|
||||
this.httpMethod = instanceSettings.jsonData.httpMethod || 'GET';
|
||||
this.id = instanceSettings.id;
|
||||
this.interval = instanceSettings.jsonData.timeInterval || '15s';
|
||||
this.lookupsDisabled = instanceSettings.jsonData.disableMetricsLookup ?? false;
|
||||
this.metricNamesAutocompleteSuggestionLimit =
|
||||
instanceSettings.jsonData.codeModeMetricNamesSuggestionLimit ?? SUGGESTIONS_LIMIT;
|
||||
this.ruleMappings = {};
|
||||
this.seriesEndpoint = instanceSettings.jsonData.seriesEndpoint ?? false;
|
||||
this.seriesLimit = instanceSettings.jsonData.seriesLimit ?? DEFAULT_SERIES_LIMIT;
|
||||
this.type = 'prometheus';
|
||||
this.url = instanceSettings.url!;
|
||||
this.withCredentials = Boolean(instanceSettings.withCredentials);
|
||||
this.defaultEditor = instanceSettings.jsonData.defaultEditor;
|
||||
|
||||
// INHERITED PROPERTIES
|
||||
this.annotations = PrometheusAnnotationSupport(this);
|
||||
this.variables = new PrometheusVariableSupport(this, this.templateSrv);
|
||||
|
||||
// LANGUAGE PROVIDER
|
||||
// This needs to be the last thing we initialize.
|
||||
this.languageProvider = languageProvider ?? new PrometheusLanguageProvider(this);
|
||||
}
|
||||
|
||||
init = async () => {
|
||||
/**
|
||||
* Initializes the Prometheus datasource by loading recording rules and checking exemplar availability.
|
||||
*
|
||||
* This method performs two key initialization tasks: Loads recording rules from the
|
||||
* Prometheus API and checks if exemplars are available by testing the exemplars API endpoint.
|
||||
*/
|
||||
init = async (): Promise<void> => {
|
||||
if (!this.disableRecordingRules) {
|
||||
this.loadRules();
|
||||
}
|
||||
this.exemplarsAvailable = await this.areExemplarsAvailable();
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads recording rules from the Prometheus API and extracts rule mappings.
|
||||
*
|
||||
* This method fetches rules from the `/api/v1/rules` endpoint and processes
|
||||
* them to create a mapping of rule names to their corresponding queries and labels.
|
||||
* The rules API is experimental, so errors are logged but not thrown.
|
||||
*/
|
||||
private async loadRules(): Promise<void> {
|
||||
try {
|
||||
const params = {};
|
||||
const options = { showErrorAlert: false };
|
||||
const res = await this.metadataRequest('/api/v1/rules', params, options);
|
||||
const ruleGroups = res.data?.data?.groups;
|
||||
|
||||
if (ruleGroups) {
|
||||
this.ruleMappings = extractRuleMappingFromGroups(ruleGroups);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Rules API is experimental. Ignore next error.');
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if exemplars are available by testing the exemplars API endpoint.
|
||||
*
|
||||
* This method makes a test request to the `/api/v1/query_exemplars` endpoint to determine
|
||||
* if the Prometheus instance supports exemplars. The test uses a simple query with a
|
||||
* 30-minute time range. If the request succeeds with a 'success' status, exemplars
|
||||
* are considered available. Errors are caught and return false to avoid breaking
|
||||
* the datasource initialization.
|
||||
*/
|
||||
private async areExemplarsAvailable(): Promise<boolean> {
|
||||
try {
|
||||
const params = {
|
||||
query: 'test',
|
||||
start: dateTime().subtract(30, 'minutes').valueOf().toString(),
|
||||
end: dateTime().valueOf().toString(),
|
||||
};
|
||||
const options = { showErrorAlert: false };
|
||||
const res = await this.metadataRequest('/api/v1/query_exemplars', params, options);
|
||||
|
||||
return res.data.status === 'success';
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getQueryDisplayText(query: PromQuery) {
|
||||
return query.expr;
|
||||
}
|
||||
@@ -604,43 +656,6 @@ export class PrometheusDatasource
|
||||
return getQueryHints(query.expr ?? '', result, this);
|
||||
}
|
||||
|
||||
async loadRules() {
|
||||
try {
|
||||
const res = await this.metadataRequest('/api/v1/rules', {}, { showErrorAlert: false });
|
||||
const groups = res.data?.data?.groups;
|
||||
|
||||
if (groups) {
|
||||
this.ruleMappings = extractRuleMappingFromGroups(groups);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Rules API is experimental. Ignore next error.');
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async areExemplarsAvailable() {
|
||||
try {
|
||||
const res = await this.metadataRequest(
|
||||
'/api/v1/query_exemplars',
|
||||
{
|
||||
query: 'test',
|
||||
start: dateTime().subtract(30, 'minutes').valueOf().toString(),
|
||||
end: dateTime().valueOf().toString(),
|
||||
},
|
||||
{
|
||||
// Avoid alerting the user if this test fails
|
||||
showErrorAlert: false,
|
||||
}
|
||||
);
|
||||
if (res.data.status === 'success') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
modifyQuery(query: PromQuery, action: QueryFixAction): PromQuery {
|
||||
let expression = query.expr ?? '';
|
||||
switch (action.type) {
|
||||
|
||||
@@ -20,7 +20,7 @@ export { PromVariableQueryEditor } from './components/VariableQueryEditor';
|
||||
// Main export
|
||||
export { ConfigEditor } from './configuration/ConfigEditor';
|
||||
export { overhaulStyles, validateInput, docsTip } from './configuration/shared/utils';
|
||||
export { PROM_CONFIG_LABEL_WIDTH } from './constants';
|
||||
export { PROM_CONFIG_LABEL_WIDTH, InstantQueryRefIdIndex } from './constants';
|
||||
// The parts
|
||||
export { AlertingSettingsOverhaul } from './configuration/AlertingSettingsOverhaul';
|
||||
export { DataSourceHttpSettingsOverhaul } from './configuration/DataSourceHttpSettingsOverhaul';
|
||||
@@ -54,7 +54,7 @@ export { MetricsModal } from './querybuilder/components/metrics-modal/MetricsMod
|
||||
|
||||
// SRC/
|
||||
// Main export
|
||||
export { PrometheusDatasource, InstantQueryRefIdIndex } from './datasource';
|
||||
export { PrometheusDatasource } from './datasource';
|
||||
// The parts
|
||||
export { addLabelToQuery } from './add_label_to_query';
|
||||
export { type QueryEditorMode, type PromQueryFormat, type Prometheus } from './dataquery';
|
||||
|
||||
Reference in New Issue
Block a user