AzureMonitor: Add switch to control time-range for Logs queries (#71278)
* Update types * Update migration - Default intersectTime property to false * Update frontend components - Add intersectTime field - Update tests - Update mocks - Add onChange function * Update backend - Appropriately set intersectTime for logs queries - intersectTime is always true for Traces queries - Update tests * Update docs * Fix test and lint
This commit is contained in:
@@ -118,6 +118,9 @@ type AppInsightsMetricNameQueryKind string
|
||||
|
||||
// Azure Monitor Logs sub-query properties
|
||||
type AzureLogsQuery struct {
|
||||
// If set to true the intersection of time ranges specified in the query and Grafana will be used. Otherwise the query time ranges will be used. Defaults to false
|
||||
IntersectTime *bool `json:"intersectTime,omitempty"`
|
||||
|
||||
// KQL query to be executed.
|
||||
Query *string `json:"query,omitempty"`
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ type AzureLogAnalyticsQuery struct {
|
||||
Resources []string
|
||||
QueryType string
|
||||
AppInsightsQuery bool
|
||||
IntersectTime bool
|
||||
}
|
||||
|
||||
func (e *AzureLogAnalyticsDatasource) ResourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client) {
|
||||
@@ -103,6 +104,7 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
traceExploreQuery := ""
|
||||
traceParentExploreQuery := ""
|
||||
traceLogsExploreQuery := ""
|
||||
intersectTime := false
|
||||
if query.QueryType == string(dataquery.AzureQueryTypeAzureLogAnalytics) {
|
||||
queryJSONModel := types.LogJSONQuery{}
|
||||
err := json.Unmarshal(query.JSON, &queryJSONModel)
|
||||
@@ -138,6 +140,10 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
if azureLogAnalyticsTarget.Query != nil {
|
||||
queryString = *azureLogAnalyticsTarget.Query
|
||||
}
|
||||
|
||||
if azureLogAnalyticsTarget.IntersectTime != nil {
|
||||
intersectTime = *azureLogAnalyticsTarget.IntersectTime
|
||||
}
|
||||
}
|
||||
|
||||
if query.QueryType == string(dataquery.AzureQueryTypeAzureTraces) {
|
||||
@@ -210,6 +216,8 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create traces logs explore query: %s", err)
|
||||
}
|
||||
|
||||
intersectTime = true
|
||||
}
|
||||
|
||||
apiURL := getApiURL(resourceOrWorkspace, appInsightsQuery)
|
||||
@@ -232,6 +240,7 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
TraceParentExploreQuery: traceParentExploreQuery,
|
||||
TraceLogsExploreQuery: traceLogsExploreQuery,
|
||||
AppInsightsQuery: appInsightsQuery,
|
||||
IntersectTime: intersectTime,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -442,12 +451,15 @@ func appendErrorNotice(frame *data.Frame, err *AzureLogAnalyticsAPIError) *data.
|
||||
}
|
||||
|
||||
func (e *AzureLogAnalyticsDatasource) createRequest(ctx context.Context, logger log.Logger, queryURL string, query *AzureLogAnalyticsQuery) (*http.Request, error) {
|
||||
from := query.TimeRange.From.Format(time.RFC3339)
|
||||
to := query.TimeRange.To.Format(time.RFC3339)
|
||||
timespan := fmt.Sprintf("%s/%s", from, to)
|
||||
body := map[string]interface{}{
|
||||
"query": query.Query,
|
||||
"timespan": timespan,
|
||||
"query": query.Query,
|
||||
}
|
||||
|
||||
if query.IntersectTime {
|
||||
from := query.TimeRange.From.Format(time.RFC3339)
|
||||
to := query.TimeRange.To.Format(time.RFC3339)
|
||||
timespan := fmt.Sprintf("%s/%s", from, to)
|
||||
body["timespan"] = timespan
|
||||
}
|
||||
|
||||
if len(query.Resources) > 1 && query.QueryType == string(dataquery.AzureQueryTypeAzureLogAnalytics) && !query.AppInsightsQuery {
|
||||
|
||||
@@ -109,7 +109,8 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"azureLogAnalytics": {
|
||||
"resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace",
|
||||
"query": "Perf | where $__timeFilter() | where $__contains(Computer, 'comp1','comp2') | summarize avg(CounterValue) by bin(TimeGenerated, $__interval), Computer",
|
||||
"resultFormat": "%s"
|
||||
"resultFormat": "%s",
|
||||
"intersectTime": false
|
||||
}
|
||||
}`, types.TimeSeries)),
|
||||
RefID: "A",
|
||||
@@ -127,7 +128,8 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"azureLogAnalytics": {
|
||||
"resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace",
|
||||
"query": "Perf | where $__timeFilter() | where $__contains(Computer, 'comp1','comp2') | summarize avg(CounterValue) by bin(TimeGenerated, $__interval), Computer",
|
||||
"resultFormat": "%s"
|
||||
"resultFormat": "%s",
|
||||
"intersectTime": false
|
||||
}
|
||||
}`, types.TimeSeries)),
|
||||
Query: "Perf | where ['TimeGenerated'] >= datetime('2018-03-15T13:00:00Z') and ['TimeGenerated'] <= datetime('2018-03-15T13:34:00Z') | where ['Computer'] in ('comp1','comp2') | summarize avg(CounterValue) by bin(TimeGenerated, 34000ms), Computer",
|
||||
@@ -135,6 +137,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureLogAnalytics),
|
||||
AppInsightsQuery: false,
|
||||
IntersectTime: false,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -172,6 +175,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
Resources: []string{},
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureLogAnalytics),
|
||||
AppInsightsQuery: false,
|
||||
IntersectTime: false,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -209,6 +213,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
Resources: []string{},
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureLogAnalytics),
|
||||
AppInsightsQuery: false,
|
||||
IntersectTime: false,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -222,7 +227,8 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"azureLogAnalytics": {
|
||||
"resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace",
|
||||
"query": "Perf",
|
||||
"resultFormat": "%s"
|
||||
"resultFormat": "%s",
|
||||
"intersectTime": false
|
||||
}
|
||||
}`, types.TimeSeries)),
|
||||
RefID: "A",
|
||||
@@ -239,13 +245,15 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"azureLogAnalytics": {
|
||||
"resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace",
|
||||
"query": "Perf",
|
||||
"resultFormat": "%s"
|
||||
"resultFormat": "%s",
|
||||
"intersectTime": false
|
||||
}
|
||||
}`, types.TimeSeries)),
|
||||
Query: "Perf",
|
||||
Resources: []string{"/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace"},
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureLogAnalytics),
|
||||
AppInsightsQuery: false,
|
||||
IntersectTime: false,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -259,7 +267,8 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"azureLogAnalytics": {
|
||||
"resources": ["/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace2"],
|
||||
"query": "Perf",
|
||||
"resultFormat": "%s"
|
||||
"resultFormat": "%s",
|
||||
"intersectTime": false
|
||||
}
|
||||
}`, types.TimeSeries)),
|
||||
RefID: "A",
|
||||
@@ -277,7 +286,8 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"azureLogAnalytics": {
|
||||
"resources": ["/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace2"],
|
||||
"query": "Perf",
|
||||
"resultFormat": "%s"
|
||||
"resultFormat": "%s",
|
||||
"intersectTime": false
|
||||
}
|
||||
}`, types.TimeSeries)),
|
||||
Query: "Perf",
|
||||
@@ -285,6 +295,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureLogAnalytics),
|
||||
AppInsightsQuery: false,
|
||||
IntersectTime: false,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -365,6 +376,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -442,6 +454,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -516,6 +529,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"${__data.fields.traceID}\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -593,6 +607,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -675,6 +690,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -757,6 +773,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -839,6 +856,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -913,6 +931,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"${__data.fields.traceID}\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -990,6 +1009,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -1035,6 +1055,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" +
|
||||
"| where operation_Id == \"test-op-id\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -1116,6 +1137,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"app('/subscriptions/test-sub/resourcegroups/test-rg/providers/microsoft.insights/components/r2').traces\n" +
|
||||
"| where operation_Id == \"op-id-multi\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -1194,6 +1216,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"app('/subscriptions/test-sub/resourcegroups/test-rg/providers/microsoft.insights/components/r2').traces\n" +
|
||||
"| where operation_Id == \"${__data.fields.traceID}\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -1275,6 +1298,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"app('/subscriptions/test-sub/resourcegroups/test-rg/providers/microsoft.insights/components/r2').traces\n" +
|
||||
"| where operation_Id == \"op-id-multi\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -1363,6 +1387,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) {
|
||||
"app('/subscriptions/test-sub/resourcegroups/test-rg/providers/microsoft.insights/components/r3').traces\n" +
|
||||
"| where operation_Id == \"op-id-non-overlapping\"",
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
},
|
||||
},
|
||||
Err: require.NoError,
|
||||
@@ -1389,6 +1414,31 @@ func TestLogAnalyticsCreateRequest(t *testing.T) {
|
||||
req, err := ds.createRequest(ctx, logger, url, &AzureLogAnalyticsQuery{
|
||||
Resources: []string{"r"},
|
||||
Query: "Perf",
|
||||
IntersectTime: false,
|
||||
AppInsightsQuery: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if req.URL.String() != url {
|
||||
t.Errorf("Expecting %s, got %s", url, req.URL.String())
|
||||
}
|
||||
expectedHeaders := http.Header{"Content-Type": []string{"application/json"}}
|
||||
if !cmp.Equal(req.Header, expectedHeaders) {
|
||||
t.Errorf("Unexpected HTTP headers: %v", cmp.Diff(req.Header, expectedHeaders))
|
||||
}
|
||||
expectedBody := `{"query":"Perf"}`
|
||||
body, err := io.ReadAll(req.Body)
|
||||
require.NoError(t, err)
|
||||
if !cmp.Equal(string(body), expectedBody) {
|
||||
t.Errorf("Unexpected Body: %v", cmp.Diff(string(body), expectedBody))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("creates a request with timespan", func(t *testing.T) {
|
||||
ds := AzureLogAnalyticsDatasource{}
|
||||
req, err := ds.createRequest(ctx, logger, url, &AzureLogAnalyticsQuery{
|
||||
Resources: []string{"r"},
|
||||
Query: "Perf",
|
||||
IntersectTime: true,
|
||||
AppInsightsQuery: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -1414,9 +1464,10 @@ func TestLogAnalyticsCreateRequest(t *testing.T) {
|
||||
Query: "Perf",
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureLogAnalytics),
|
||||
AppInsightsQuery: false,
|
||||
IntersectTime: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
expectedBody := `{"query":"Perf","timespan":"0001-01-01T00:00:00Z/0001-01-01T00:00:00Z","workspaces":["/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.OperationalInsights/workspaces/r1","/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.OperationalInsights/workspaces/r2"]}`
|
||||
expectedBody := `{"query":"Perf","workspaces":["/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.OperationalInsights/workspaces/r1","/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.OperationalInsights/workspaces/r2"]}`
|
||||
body, err := io.ReadAll(req.Body)
|
||||
require.NoError(t, err)
|
||||
if !cmp.Equal(string(body), expectedBody) {
|
||||
@@ -1437,6 +1488,7 @@ func TestLogAnalyticsCreateRequest(t *testing.T) {
|
||||
To: to,
|
||||
},
|
||||
AppInsightsQuery: false,
|
||||
IntersectTime: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
expectedBody := fmt.Sprintf(`{"query":"Perf","timespan":"%s/%s","workspaces":["/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.OperationalInsights/workspaces/r1","/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.OperationalInsights/workspaces/r2"]}`, from.Format(time.RFC3339), to.Format(time.RFC3339))
|
||||
@@ -1459,6 +1511,7 @@ func TestLogAnalyticsCreateRequest(t *testing.T) {
|
||||
To: to,
|
||||
},
|
||||
AppInsightsQuery: true,
|
||||
IntersectTime: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
expectedBody := fmt.Sprintf(`{"applications":["/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.Insights/components/r1","/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.Insights/components/r2"],"query":"","timespan":"%s/%s"}`, from.Format(time.RFC3339), to.Format(time.RFC3339))
|
||||
|
||||
Reference in New Issue
Block a user