From a39c486f316ab703e5df68ce9e6a12ec116e1fd3 Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Tue, 9 May 2023 17:02:19 +0100 Subject: [PATCH] [v10.0.x] AzureMonitor: Appropriately handle trace correlation resources (#68108) AzureMonitor: Appropriately handle trace correlation resources (#67934) * Appropriately handle correlation resources - Retrieve resources in buildQueries step - Build updated queries based off of correlation results - Update tests * Fix query building and tests * Fix lint * Ensure multiple resource queries will work - Correctly set resources to ensure there is no overlapping between multiple resource selection and correlated resources - Add tests for these specific cases - Remove unnecessary pointers - Build queries correctly (cherry picked from commit 5ddc37ee21a5a10862ee44604674a4996e6ccd48) Co-authored-by: Andreas Christou --- .../azure-log-analytics-datasource.go | 102 +++-- .../azure-log-analytics-datasource_test.go | 412 ++++++++++++++++-- 2 files changed, 444 insertions(+), 70 deletions(-) diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go index 6ad2b8b5bae..edeaa65e84f 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go @@ -59,7 +59,7 @@ func (e *AzureLogAnalyticsDatasource) ResourceRequest(rw http.ResponseWriter, re func (e *AzureLogAnalyticsDatasource) ExecuteTimeSeriesQuery(ctx context.Context, logger log.Logger, originalQueries []backend.DataQuery, dsInfo types.DatasourceInfo, client *http.Client, url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) { result := backend.NewQueryDataResponse() ctxLogger := logger.FromContext(ctx) - queries, err := e.buildQueries(ctxLogger, originalQueries, dsInfo) + queries, err := e.buildQueries(ctx, ctxLogger, originalQueries, dsInfo, tracer) if err != nil { return nil, err } @@ -81,7 +81,7 @@ func getApiURL(resourceOrWorkspace string) string { } } -func (e *AzureLogAnalyticsDatasource) buildQueries(logger log.Logger, queries []backend.DataQuery, dsInfo types.DatasourceInfo) ([]*AzureLogAnalyticsQuery, error) { +func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger log.Logger, queries []backend.DataQuery, dsInfo types.DatasourceInfo, tracer tracing.Tracer) ([]*AzureLogAnalyticsQuery, error) { azureLogAnalyticsQueries := []*AzureLogAnalyticsQuery{} for _, query := range queries { @@ -144,20 +144,32 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(logger log.Logger, queries [] resources = azureTracesTarget.Resources resourceOrWorkspace = azureTracesTarget.Resources[0] - - operationId := "" - if queryJSONModel.AzureTraces.OperationId != nil { - operationId = *queryJSONModel.AzureTraces.OperationId + resourcesMap := make(map[string]bool, 0) + if len(resources) > 1 { + for _, resource := range resources { + resourcesMap[strings.ToLower(resource)] = true + } + // Remove the base resource as that's where the query is run anyway + delete(resourcesMap, strings.ToLower(resourceOrWorkspace)) } - queryString = buildTracesQuery(operationId, queryJSONModel.AzureTraces.TraceTypes, queryJSONModel.AzureTraces.Filters, &resultFormat) + operationId := "" + if queryJSONModel.AzureTraces.OperationId != nil && *queryJSONModel.AzureTraces.OperationId != "" { + operationId = *queryJSONModel.AzureTraces.OperationId + resourcesMap, err = getCorrelationWorkspaces(ctx, logger, resourceOrWorkspace, resourcesMap, dsInfo, operationId, tracer) + if err != nil { + return nil, fmt.Errorf("failed to retrieve correlation resources for operation ID - %s: %s", operationId, err) + } + } + + queryString = buildTracesQuery(operationId, queryJSONModel.AzureTraces.TraceTypes, queryJSONModel.AzureTraces.Filters, &resultFormat, resourcesMap) traceIdVariable := "${__data.fields.traceID}" if operationId == "" { - traceExploreQuery = buildTracesQuery(traceIdVariable, queryJSONModel.AzureTraces.TraceTypes, queryJSONModel.AzureTraces.Filters, nil) - traceLogsExploreQuery = buildTracesLogsQuery(&traceIdVariable) + traceExploreQuery = buildTracesQuery(traceIdVariable, queryJSONModel.AzureTraces.TraceTypes, queryJSONModel.AzureTraces.Filters, &resultFormat, resourcesMap) + traceLogsExploreQuery = buildTracesLogsQuery(traceIdVariable, resourcesMap) } else { traceExploreQuery = queryString - traceLogsExploreQuery = buildTracesLogsQuery(&operationId) + traceLogsExploreQuery = buildTracesLogsQuery(operationId, resourcesMap) } traceExploreQuery, err = macros.KqlInterpolate(logger, query, dsInfo, traceExploreQuery, "TimeGenerated") if err != nil { @@ -223,13 +235,6 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, logger l } if query.QueryType == string(dataquery.AzureQueryTypeAzureTraces) { - if queryJSONModel.AzureTraces.OperationId != nil && *queryJSONModel.AzureTraces.OperationId != "" { - query, err = getCorrelationWorkspaces(ctx, logger, query, dsInfo, *queryJSONModel.AzureTraces.OperationId, tracer) - if err != nil { - dataResponse.Error = err - return dataResponse - } - } if dataquery.ResultFormat(query.ResultFormat) == (dataquery.ResultFormatTrace) && query.Query == "" { return dataResponseErrorWithExecuted(fmt.Errorf("cannot visualise trace events using the trace visualiser")) } @@ -484,9 +489,9 @@ func getTracesQueryUrl(resources []string, azurePortalUrl string) (string, error return portalUrl, nil } -func getCorrelationWorkspaces(ctx context.Context, logger log.Logger, query *AzureLogAnalyticsQuery, dsInfo types.DatasourceInfo, operationId string, tracer tracing.Tracer) (*AzureLogAnalyticsQuery, error) { +func getCorrelationWorkspaces(ctx context.Context, logger log.Logger, baseResource string, resourcesMap map[string]bool, dsInfo types.DatasourceInfo, operationId string, tracer tracing.Tracer) (map[string]bool, error) { azMonService := dsInfo.Services["Azure Monitor"] - correlationUrl := azMonService.URL + fmt.Sprintf("%s/providers/microsoft.insights/transactions/%s", query.Resources[0], operationId) + correlationUrl := azMonService.URL + fmt.Sprintf("%s/providers/microsoft.insights/transactions/%s", baseResource, operationId) callCorrelationAPI := func(url string) (AzureCorrelationAPIResponse, error) { req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer([]byte{})) @@ -539,6 +544,12 @@ func getCorrelationWorkspaces(ctx context.Context, logger log.Logger, query *Azu return AzureCorrelationAPIResponse{}, err } + for _, resource := range data.Properties.Resources { + lowerCaseResource := strings.ToLower(resource) + if _, ok := resourcesMap[lowerCaseResource]; !ok { + resourcesMap[lowerCaseResource] = true + } + } return data, nil } @@ -547,21 +558,21 @@ func getCorrelationWorkspaces(ctx context.Context, logger log.Logger, query *Azu correlationResponse, err := callCorrelationAPI(correlationUrl) if err != nil { - return query, err + return nil, err } nextLink = correlationResponse.Properties.NextLink - query.Resources = correlationResponse.Properties.Resources for nextLink != nil { correlationResponse, err := callCorrelationAPI(correlationUrl) if err != nil { - return query, err + return nil, err } - query.Resources = append(query.Resources, correlationResponse.Properties.Resources...) nextLink = correlationResponse.Properties.NextLink } - return query, nil + // Remove the base element as that's where the query is run anyway + delete(resourcesMap, strings.ToLower(baseResource)) + return resourcesMap, nil } // Error definition has been inferred from real data and other model definitions like @@ -663,7 +674,7 @@ func encodeQuery(rawQuery string) (string, error) { return base64.StdEncoding.EncodeToString(b.Bytes()), nil } -func buildTracesQuery(operationId string, traceTypes []string, filters []types.TracesFilters, resultFormat *string) string { +func buildTracesQuery(operationId string, traceTypes []string, filters []types.TracesFilters, resultFormat *string, resources map[string]bool) string { types := traceTypes if len(types) == 0 { types = Tables @@ -682,6 +693,19 @@ func buildTracesQuery(operationId string, traceTypes []string, filters []types.T return "" } + resourcesQuery := strings.Join(filteredTypes, ",") + if len(resources) > 0 { + intermediate := make([]string, 0) + for resource := range resources { + resourceSplit := strings.SplitAfter(resource, "/") + resourceName := resourceSplit[len(resourceSplit)-1] + for _, table := range filteredTypes { + intermediate = append(intermediate, fmt.Sprintf("app('%s').%s", resourceName, table)) + } + } + resourcesQuery += "," + strings.Join(intermediate, ",") + } + tagsMap := make(map[string]bool) var tags []string for _, t := range filteredTypes { @@ -734,7 +758,7 @@ func buildTracesQuery(operationId string, traceTypes []string, filters []types.T errorProperty = `| extend error = iff(itemType == "exceptions", true, false)` } - baseQuery := fmt.Sprintf(`set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true %s | where $__timeFilter()`, strings.Join(filteredTypes, ",")) + baseQuery := fmt.Sprintf(`set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true %s`, resourcesQuery) propertiesStaticQuery := `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + @@ -747,9 +771,25 @@ func buildTracesQuery(operationId string, traceTypes []string, filters []types.T return baseQuery + whereClause + propertiesStaticQuery + propertiesQuery + errorProperty + filtersClause + projectClause } -func buildTracesLogsQuery(operationId *string) string { - tables := `union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults` - time := `| where $__timeFilter()` - operationIdFilter := fmt.Sprintf(`| where operation_Id == "%s"`, *operationId) - return strings.Join([]string{tables, time, operationIdFilter}, " \n") +func buildTracesLogsQuery(operationId string, resources map[string]bool) string { + types := Tables + sort.Strings(types) + selectors := "union " + strings.Join(types, ",\n") + "\n" + if len(resources) > 0 { + intermediate := make([]string, 0) + for resource := range resources { + resourceSplit := strings.SplitAfter(resource, "/") + resourceName := resourceSplit[len(resourceSplit)-1] + for _, table := range types { + intermediate = append(intermediate, fmt.Sprintf("app('%s').%s", resourceName, table)) + } + } + sort.Strings(intermediate) + types = intermediate + selectors = strings.Join(append([]string{"union *"}, types...), ",\n") + "\n" + } + + query := selectors + query += fmt.Sprintf(`| where operation_Id == "%s"`, operationId) + return query } diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go index 98ae108cd25..b69c9ccea34 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go @@ -2,15 +2,18 @@ package loganalytics import ( "context" + "encoding/json" "fmt" "io" "net/http" + "net/http/httptest" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/infra/log" @@ -25,6 +28,71 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { datasource := &AzureLogAnalyticsDatasource{} fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local) timeRange := backend.TimeRange{From: fromStart, To: fromStart.Add(34 * time.Minute)} + ctx := context.Background() + tracer := tracing.InitializeTracerForTest() + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + var correlationRes AzureCorrelationAPIResponse + if strings.Contains(r.URL.Path, "test-op-id") { + correlationRes = AzureCorrelationAPIResponse{ + ID: "/subscriptions/r1", + Name: "guid-1", + Type: "microsoft.insights/transactions", + Properties: AzureCorrelationAPIResponseProperties{ + Resources: []string{ + "/subscriptions/r1", + }, + NextLink: nil, + }, + } + } else if strings.Contains(r.URL.Path, "op-id-multi") { + correlationRes = AzureCorrelationAPIResponse{ + ID: "/subscriptions/r1", + Name: "guid-1", + Type: "microsoft.insights/transactions", + Properties: AzureCorrelationAPIResponseProperties{ + Resources: []string{ + "/subscriptions/r1", + "/subscriptions/r2", + }, + NextLink: nil, + }, + } + } else if strings.Contains(r.URL.Path, "op-id-non-overlapping") { + correlationRes = AzureCorrelationAPIResponse{ + ID: "/subscriptions/r1", + Name: "guid-1", + Type: "microsoft.insights/transactions", + Properties: AzureCorrelationAPIResponseProperties{ + Resources: []string{ + "/subscriptions/r1", + "/subscriptions/r3", + }, + NextLink: nil, + }, + } + } + err := json.NewEncoder(w).Encode(correlationRes) + if err != nil { + t.Errorf("failed to encode correlation API response") + } + })) + + provider := httpclient.NewProvider(httpclient.ProviderOptions{Timeout: &httpclient.DefaultTimeoutOptions}) + client, err := provider.New() + if err != nil { + t.Errorf("failed to create fake client") + } + + dsInfo := types.DatasourceInfo{ + Services: map[string]types.DatasourceService{ + "Azure Monitor": {URL: svr.URL, HTTPClient: client}, + }, + JSONData: map[string]interface{}{ + "azureLogAnalyticsSameAs": false, + }, + } tests := []struct { name string @@ -249,7 +317,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "operationId": "test-op-id" } }`, dataquery.ResultFormatTable)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -263,7 +331,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -274,8 +342,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, @@ -311,7 +378,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "operationId": "test-op-id" } }`), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -325,7 +392,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true trace` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -336,8 +403,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, @@ -371,7 +437,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "resultFormat": "%s" } }`, dataquery.ResultFormatTable)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + @@ -385,7 +451,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == '${__data.fields.traceID}') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == '${__data.fields.traceID}')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -397,8 +463,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"${__data.fields.traceID}\"", }, }, @@ -434,7 +499,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "operationId": "test-op-id" } }`, dataquery.ResultFormatTable)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -449,7 +514,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -461,8 +526,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, @@ -500,7 +564,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "filters": [{"filters": ["test-app-id"], "property": "appId", "operation": "eq"}] } }`, dataquery.ResultFormatTable)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -516,7 +580,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -529,8 +593,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, @@ -568,7 +631,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "filters": [{"filters": ["test-app-id"], "property": "appId", "operation": "ne"}] } }`, dataquery.ResultFormatTable)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -584,7 +647,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -597,8 +660,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, @@ -636,7 +698,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "filters": [{"filters": ["test-app-id"], "property": "appId", "operation": "ne"},{"filters": ["test-client-id"], "property": "clientId", "operation": "eq"}] } }`, dataquery.ResultFormatTable)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -652,7 +714,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -665,8 +727,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, @@ -700,7 +761,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "resultFormat": "%s" } }`, dataquery.ResultFormatTrace)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + @@ -714,7 +775,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,traces | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests` + `| where (operation_Id != '' and operation_Id == '${__data.fields.traceID}') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == '${__data.fields.traceID}')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -726,8 +787,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"${__data.fields.traceID}\"", }, }, @@ -763,7 +823,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { "resultFormat": "%s" } }`, dataquery.ResultFormatTrace)), - Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -778,7 +838,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { Resources: []string{"/subscriptions/r1"}, TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), - TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests | where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z')` + + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests` + `| where (operation_Id != '' and operation_Id == 'test-op-id') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'test-op-id')` + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + @@ -790,8 +850,7 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + `| order by startTime asc`, - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, @@ -834,18 +893,293 @@ func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { TimeRange: timeRange, QueryType: string(dataquery.AzureQueryTypeAzureTraces), TraceExploreQuery: "", - TraceLogsExploreQuery: "union *, traces, customEvents, pageViews, requests, dependencies, exceptions, customMetrics, availabilityResults \n" + - "| where ['timestamp'] >= datetime('2018-03-15T13:00:00Z') and ['timestamp'] <= datetime('2018-03-15T13:34:00Z') \n" + + TraceLogsExploreQuery: "union availabilityResults,\n" + "customEvents,\n" + "dependencies,\n" + "exceptions,\n" + "pageViews,\n" + "requests,\n" + "traces\n" + "| where operation_Id == \"test-op-id\"", }, }, Err: require.NoError, }, + { + name: "trace query with operation ID and correlated workspaces", + queryModel: []backend.DataQuery{ + { + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "operationId": "op-id-multi", + "resources": ["/subscriptions/r1"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + RefID: "A", + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + }, + }, + azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ + { + RefID: "A", + ResultFormat: string(dataquery.ResultFormatTrace), + URL: "v1/subscriptions/r1/query", + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "operationId": "op-id-multi", + "resources": ["/subscriptions/r1"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests` + + `| where (operation_Id != '' and operation_Id == 'op-id-multi') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'op-id-multi')` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + Resources: []string{"/subscriptions/r1"}, + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests` + + `| where (operation_Id != '' and operation_Id == 'op-id-multi') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'op-id-multi')` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + TraceLogsExploreQuery: "union *,\n" + + "app('r2').availabilityResults,\n" + + "app('r2').customEvents,\n" + + "app('r2').dependencies,\n" + + "app('r2').exceptions,\n" + + "app('r2').pageViews,\n" + + "app('r2').requests,\n" + + "app('r2').traces\n" + + "| where operation_Id == \"op-id-multi\"", + }, + }, + Err: require.NoError, + }, + { + name: "trace query with multiple resources", + queryModel: []backend.DataQuery{ + { + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "resources": ["/subscriptions/r1", "/subscriptions/r2"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + RefID: "A", + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + }, + }, + azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ + { + RefID: "A", + ResultFormat: string(dataquery.ResultFormatTrace), + URL: "v1/subscriptions/r1/query", + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "resources": ["/subscriptions/r1", "/subscriptions/r2"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + Resources: []string{"/subscriptions/r1", "/subscriptions/r2"}, + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests` + + `| where (operation_Id != '' and operation_Id == '${__data.fields.traceID}') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == '${__data.fields.traceID}')` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + TraceLogsExploreQuery: "union *,\n" + + "app('r2').availabilityResults,\n" + + "app('r2').customEvents,\n" + + "app('r2').dependencies,\n" + + "app('r2').exceptions,\n" + + "app('r2').pageViews,\n" + + "app('r2').requests,\n" + + "app('r2').traces\n" + + "| where operation_Id == \"${__data.fields.traceID}\"", + }, + }, + Err: require.NoError, + }, + { + name: "trace query with multiple resources and overlapping correlated workspaces", + queryModel: []backend.DataQuery{ + { + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "operationId": "op-id-multi", + "resources": ["/subscriptions/r1", "/subscriptions/r2"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + RefID: "A", + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + }, + }, + azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ + { + RefID: "A", + ResultFormat: string(dataquery.ResultFormatTrace), + URL: "v1/subscriptions/r1/query", + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "operationId": "op-id-multi", + "resources": ["/subscriptions/r1", "/subscriptions/r2"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests` + + `| where (operation_Id != '' and operation_Id == 'op-id-multi') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'op-id-multi')` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + Resources: []string{"/subscriptions/r1", "/subscriptions/r2"}, + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests` + + `| where (operation_Id != '' and operation_Id == 'op-id-multi') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'op-id-multi')` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + TraceLogsExploreQuery: "union *,\n" + + "app('r2').availabilityResults,\n" + + "app('r2').customEvents,\n" + + "app('r2').dependencies,\n" + + "app('r2').exceptions,\n" + + "app('r2').pageViews,\n" + + "app('r2').requests,\n" + + "app('r2').traces\n" + + "| where operation_Id == \"op-id-multi\"", + }, + }, + Err: require.NoError, + }, + { + name: "trace query with multiple resources and non-overlapping correlated workspaces", + queryModel: []backend.DataQuery{ + { + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "operationId": "op-id-non-overlapping", + "resources": ["/subscriptions/r1", "/subscriptions/r2"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + RefID: "A", + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + }, + }, + azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ + { + RefID: "A", + ResultFormat: string(dataquery.ResultFormatTrace), + URL: "v1/subscriptions/r1/query", + JSON: []byte(fmt.Sprintf(`{ + "queryType": "Azure Traces", + "azureTraces": { + "operationId": "op-id-non-overlapping", + "resources": ["/subscriptions/r1", "/subscriptions/r2"], + "resultFormat": "%s" + } + }`, dataquery.ResultFormatTrace)), + Query: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests,app('r3').availabilityResults,app('r3').customEvents,app('r3').dependencies,app('r3').exceptions,app('r3').pageViews,app('r3').requests` + + `| where (operation_Id != '' and operation_Id == 'op-id-non-overlapping') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'op-id-non-overlapping')` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + Resources: []string{"/subscriptions/r1", "/subscriptions/r2"}, + TimeRange: timeRange, + QueryType: string(dataquery.AzureQueryTypeAzureTraces), + TraceExploreQuery: `set truncationmaxrecords=10000; set truncationmaxsize=67108864; union isfuzzy=true availabilityResults,customEvents,dependencies,exceptions,pageViews,requests,app('r2').availabilityResults,app('r2').customEvents,app('r2').dependencies,app('r2').exceptions,app('r2').pageViews,app('r2').requests,app('r3').availabilityResults,app('r3').customEvents,app('r3').dependencies,app('r3').exceptions,app('r3').pageViews,app('r3').requests` + + `| where (operation_Id != '' and operation_Id == 'op-id-non-overlapping') or (customDimensions.ai_legacyRootId != '' and customDimensions.ai_legacyRootId == 'op-id-non-overlapping')` + + `| extend duration = iff(isnull(column_ifexists("duration", real(null))), toreal(0), column_ifexists("duration", real(null)))` + + `| extend spanID = iff(itemType == "pageView" or isempty(column_ifexists("id", "")), tostring(new_guid()), column_ifexists("id", ""))` + + `| extend operationName = iff(isempty(column_ifexists("name", "")), column_ifexists("problemId", ""), column_ifexists("name", ""))` + + `| extend serviceName = cloud_RoleName| extend serviceTags = bag_pack_columns(cloud_RoleInstance, cloud_RoleName)` + + `| extend tags = bag_merge(bag_pack_columns(appId,appName,application_Version,assembly,client_Browser,client_City,client_CountryOrRegion,client_IP,client_Model,client_OS,client_StateOrProvince,client_Type,data,details,duration,handledAt,iKey,id,innermostAssembly,innermostMessage,innermostMethod,innermostType,itemCount,itemId,itemType,location,message,method,name,operation_Id,operation_Name,operation_ParentId,operation_SyntheticSource,outerAssembly,outerMessage,outerMethod,outerType,performanceBucket,problemId,resultCode,sdkVersion,session_Id,severityLevel,size,source,success,target,timestamp,type,url,user_AccountId,user_AuthenticatedId,user_Id), customDimensions, customMeasurements)` + + `| extend error = iff(itemType == "exceptions", true, false)` + + `| project-rename traceID = operation_Id, parentSpanID = operation_ParentId, startTime = timestamp` + + `| project startTime, itemType, serviceName, duration, traceID, spanID, parentSpanID, operationName, serviceTags, tags, itemId` + + `| order by startTime asc`, + TraceLogsExploreQuery: "union *,\n" + + "app('r2').availabilityResults,\n" + + "app('r2').customEvents,\n" + + "app('r2').dependencies,\n" + + "app('r2').exceptions,\n" + + "app('r2').pageViews,\n" + + "app('r2').requests,\n" + + "app('r2').traces,\n" + + "app('r3').availabilityResults,\n" + + "app('r3').customEvents,\n" + + "app('r3').dependencies,\n" + + "app('r3').exceptions,\n" + + "app('r3').pageViews,\n" + + "app('r3').requests,\n" + + "app('r3').traces\n" + + "| where operation_Id == \"op-id-non-overlapping\"", + }, + }, + Err: require.NoError, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - queries, err := datasource.buildQueries(logger, tt.queryModel, types.DatasourceInfo{}) + queries, err := datasource.buildQueries(ctx, logger, tt.queryModel, dsInfo, tracer) tt.Err(t, err) if diff := cmp.Diff(tt.azureLogAnalyticsQueries[0], queries[0]); diff != "" { t.Errorf("Result mismatch (-want +got): \n%s", diff)