mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Default to /labels
API with query
param instead of /series
API (#97935)
* feat(loki-labels-api): add feature toggle * feat(loki-labels-api): default to `/labels` API
This commit is contained in:
parent
4550cfb5b7
commit
5ac7443fce
@ -84,6 +84,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
||||
| `zipkinBackendMigration` | Enables querying Zipkin data source without the proxy | Yes |
|
||||
| `alertingUIOptimizeReducer` | Enables removing the reducer from the alerting UI when creating a new alert rule and using instant query | Yes |
|
||||
| `azureMonitorEnableUserAuth` | Enables user auth for Azure Monitor datasource only | Yes |
|
||||
| `lokiLabelNamesQueryApi` | Defaults to using the Loki `/labels` API instead of `/series` | Yes |
|
||||
|
||||
## Public preview feature toggles
|
||||
|
||||
|
@ -244,4 +244,5 @@ export interface FeatureToggles {
|
||||
feedbackButton?: boolean;
|
||||
elasticsearchCrossClusterSearch?: boolean;
|
||||
unifiedHistory?: boolean;
|
||||
lokiLabelNamesQueryApi?: boolean;
|
||||
}
|
||||
|
@ -1689,6 +1689,13 @@ var (
|
||||
Owner: grafanaFrontendPlatformSquad,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "lokiLabelNamesQueryApi",
|
||||
Description: "Defaults to using the Loki `/labels` API instead of `/series`",
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
Owner: grafanaObservabilityLogsSquad,
|
||||
Expression: "true",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -225,3 +225,4 @@ alertingNotificationsStepMode,experimental,@grafana/alerting-squad,false,false,t
|
||||
feedbackButton,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
elasticsearchCrossClusterSearch,preview,@grafana/aws-datasources,false,false,false
|
||||
unifiedHistory,experimental,@grafana/grafana-frontend-platform,false,false,true
|
||||
lokiLabelNamesQueryApi,GA,@grafana/observability-logs,false,false,false
|
||||
|
|
@ -910,4 +910,8 @@ const (
|
||||
// FlagUnifiedHistory
|
||||
// Displays the navigation history so the user can navigate back to previous pages
|
||||
FlagUnifiedHistory = "unifiedHistory"
|
||||
|
||||
// FlagLokiLabelNamesQueryApi
|
||||
// Defaults to using the Loki `/labels` API instead of `/series`
|
||||
FlagLokiLabelNamesQueryApi = "lokiLabelNamesQueryApi"
|
||||
)
|
||||
|
@ -2095,6 +2095,19 @@
|
||||
"codeowner": "@grafana/observability-logs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "lokiLabelNamesQueryApi",
|
||||
"resourceVersion": "1734096677730",
|
||||
"creationTimestamp": "2024-12-13T13:31:17Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Defaults to using the Loki `/labels` API instead of `/series`",
|
||||
"stage": "GA",
|
||||
"codeowner": "@grafana/observability-logs",
|
||||
"expression": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "lokiLogsDataplane",
|
||||
|
@ -428,17 +428,51 @@ describe('Language completion provider', () => {
|
||||
expect(instance.request).toHaveBeenCalledWith('labels', datasourceWithLabels.getTimeRangeParams(mockTimeRange));
|
||||
});
|
||||
|
||||
it('should use series endpoint for request with stream selector', async () => {
|
||||
const datasourceWithLabels = setup({});
|
||||
datasourceWithLabels.languageProvider.request = jest.fn();
|
||||
describe('without labelNames feature toggle', () => {
|
||||
const lokiLabelNamesQueryApi = config.featureToggles.lokiLabelNamesQueryApi;
|
||||
beforeAll(() => {
|
||||
config.featureToggles.lokiLabelNamesQueryApi = false;
|
||||
});
|
||||
afterAll(() => {
|
||||
config.featureToggles.lokiLabelNamesQueryApi = lokiLabelNamesQueryApi;
|
||||
});
|
||||
|
||||
const instance = new LanguageProvider(datasourceWithLabels);
|
||||
instance.request = jest.fn();
|
||||
await instance.fetchLabels({ streamSelector: '{foo="bar"}' });
|
||||
expect(instance.request).toHaveBeenCalledWith('series', {
|
||||
end: 1560163909000,
|
||||
'match[]': '{foo="bar"}',
|
||||
start: 1560153109000,
|
||||
it('should use series endpoint for request with stream selector', async () => {
|
||||
const datasourceWithLabels = setup({});
|
||||
datasourceWithLabels.languageProvider.request = jest.fn();
|
||||
|
||||
const instance = new LanguageProvider(datasourceWithLabels);
|
||||
instance.request = jest.fn();
|
||||
await instance.fetchLabels({ streamSelector: '{foo="bar"}' });
|
||||
expect(instance.request).toHaveBeenCalledWith('series', {
|
||||
end: 1560163909000,
|
||||
'match[]': '{foo="bar"}',
|
||||
start: 1560153109000,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with labelNames feature toggle', () => {
|
||||
const lokiLabelNamesQueryApi = config.featureToggles.lokiLabelNamesQueryApi;
|
||||
beforeAll(() => {
|
||||
config.featureToggles.lokiLabelNamesQueryApi = true;
|
||||
});
|
||||
afterAll(() => {
|
||||
config.featureToggles.lokiLabelNamesQueryApi = lokiLabelNamesQueryApi;
|
||||
});
|
||||
|
||||
it('should use series endpoint for request with stream selector', async () => {
|
||||
const datasourceWithLabels = setup({});
|
||||
datasourceWithLabels.languageProvider.request = jest.fn();
|
||||
|
||||
const instance = new LanguageProvider(datasourceWithLabels);
|
||||
instance.request = jest.fn();
|
||||
await instance.fetchLabels({ streamSelector: '{foo="bar"}' });
|
||||
expect(instance.request).toHaveBeenCalledWith('labels', {
|
||||
end: 1560163909000,
|
||||
query: '{foo="bar"}',
|
||||
start: 1560153109000,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -147,8 +147,8 @@ export default class LokiLanguageProvider extends LanguageProvider {
|
||||
* @throws An error if the fetch operation fails.
|
||||
*/
|
||||
async fetchLabels(options?: { streamSelector?: string; timeRange?: TimeRange }): Promise<string[]> {
|
||||
// If there is no stream selector - use /labels endpoint (https://github.com/grafana/loki/pull/11982)
|
||||
if (!options || !options.streamSelector) {
|
||||
// We'll default to use `/labels`. If the flag is disabled, and there's a streamSelector, we'll use the series endpoint.
|
||||
if (config.featureToggles.lokiLabelNamesQueryApi || !options?.streamSelector) {
|
||||
return this.fetchLabelsByLabelsEndpoint(options);
|
||||
} else {
|
||||
const data = await this.fetchSeriesLabels(options.streamSelector, { timeRange: options.timeRange });
|
||||
@ -166,14 +166,20 @@ export default class LokiLanguageProvider extends LanguageProvider {
|
||||
* @returns A promise containing an array of label keys.
|
||||
* @throws An error if the fetch operation fails.
|
||||
*/
|
||||
private async fetchLabelsByLabelsEndpoint(options?: { timeRange?: TimeRange }): Promise<string[]> {
|
||||
private async fetchLabelsByLabelsEndpoint(options?: {
|
||||
streamSelector?: string;
|
||||
timeRange?: TimeRange;
|
||||
}): Promise<string[]> {
|
||||
const url = 'labels';
|
||||
const range = options?.timeRange ?? this.getDefaultTimeRange();
|
||||
const timeRange = this.datasource.getTimeRangeParams(range);
|
||||
|
||||
const res = await this.request(url, timeRange);
|
||||
const { start, end } = this.datasource.getTimeRangeParams(range);
|
||||
const params: Record<string, string | number> = { start, end };
|
||||
if (options?.streamSelector) {
|
||||
params['query'] = options.streamSelector;
|
||||
}
|
||||
const res = await this.request(url, params);
|
||||
if (Array.isArray(res)) {
|
||||
const labels = res
|
||||
const labels = Array.from(new Set(res))
|
||||
.slice()
|
||||
.sort()
|
||||
.filter((label: string) => label.startsWith('__') === false);
|
||||
|
Loading…
Reference in New Issue
Block a user