From b429aa11fe6fd1a80347de0574768d6185560487 Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Fri, 24 Jun 2022 03:13:15 -0400 Subject: [PATCH] AzureMonitor: Parse non-fatal errors for Logs (#51320) (#51362) (cherry picked from commit b10ddfdf8c4213e944f5d3f902d71df43a82e581) Co-authored-by: Andres Martinez Gotor --- .../azure-log-analytics-datasource.go | 26 +++++++++++- .../azure-log-analytics-datasource_test.go | 2 +- .../azure-response-table-frame.go | 38 +++++++++++++++++- .../azure-response-table-frame_test.go | 32 ++++++++++++++- .../azure-resource-graph-datasource.go | 2 +- .../10-log-analytics-response-warning.json | 40 +++++++++++++++++++ .../9-log-analytics-response-error.json | 40 +++++++++++++++++++ 7 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 pkg/tsdb/azuremonitor/testdata/loganalytics/10-log-analytics-response-warning.json create mode 100644 pkg/tsdb/azuremonitor/testdata/loganalytics/9-log-analytics-response-error.json diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go index 6370b36392d..8350a027fcc 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go @@ -149,7 +149,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A // If azureLogAnalyticsSameAs is defined and set to false, return an error if sameAs, ok := dsInfo.JSONData["azureLogAnalyticsSameAs"]; ok && !sameAs.(bool) { - return dataResponseErrorWithExecuted(fmt.Errorf("Log Analytics credentials are no longer supported. Go to the data source configuration to update Azure Monitor credentials")) //nolint:golint,stylecheck + return dataResponseErrorWithExecuted(fmt.Errorf("credentials for Log Analytics are no longer supported. Go to the data source configuration to update Azure Monitor credentials")) } req, err := e.createRequest(ctx, dsInfo, url) @@ -188,7 +188,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A return dataResponseErrorWithExecuted(err) } - frame, err := ResponseTableToFrame(t) + frame, err := ResponseTableToFrame(t, logResponse) if err != nil { return dataResponseErrorWithExecuted(err) } @@ -235,9 +235,31 @@ func (e *AzureLogAnalyticsDatasource) createRequest(ctx context.Context, dsInfo return req, nil } +// Error definition has been inferred from real data and other model definitions like +// https://github.com/Azure/azure-sdk-for-go/blob/3640559afddbad452d265b54fb1c20b30be0b062/services/preview/virtualmachineimagebuilder/mgmt/2019-05-01-preview/virtualmachineimagebuilder/models.go +type AzureLogAnalyticsAPIError struct { + Details *[]AzureLogAnalyticsAPIErrorBase `json:"details,omitempty"` + Code *string `json:"code,omitempty"` + Message *string `json:"message,omitempty"` +} + +type AzureLogAnalyticsAPIErrorBase struct { + Code *string `json:"code,omitempty"` + Message *string `json:"message,omitempty"` + Innererror *AzureLogAnalyticsInnerError `json:"innererror,omitempty"` +} + +type AzureLogAnalyticsInnerError struct { + Code *string `json:"code,omitempty"` + Message *string `json:"message,omitempty"` + Severity *int `json:"severity,omitempty"` + SeverityName *string `json:"severityName,omitempty"` +} + // AzureLogAnalyticsResponse is the json response object from the Azure Log Analytics API. type AzureLogAnalyticsResponse struct { Tables []types.AzureResponseTable `json:"tables"` + Error *AzureLogAnalyticsAPIError `json:"error,omitempty"` } // GetPrimaryResultTable returns the first table in the response named "PrimaryResult", or an 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 95b8c14f3af..7a7158405cd 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource_test.go @@ -236,7 +236,7 @@ func Test_executeQueryErrorWithDifferentLogAnalyticsCreds(t *testing.T) { if res.Error == nil { t.Fatal("expecting an error") } - if !strings.Contains(res.Error.Error(), "Log Analytics credentials are no longer supported") { + if !strings.Contains(res.Error.Error(), "credentials for Log Analytics are no longer supported") { t.Error("expecting the error to inform of bad credentials") } } diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame.go b/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame.go index e26b336c794..afb122cd539 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame.go @@ -5,14 +5,45 @@ import ( "fmt" "math" "strconv" + "strings" "time" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" ) +func apiErrorToNotice(err *AzureLogAnalyticsAPIError) data.Notice { + message := []string{} + severity := data.NoticeSeverityWarning + if err.Message != nil { + message = append(message, *err.Message) + } + if err.Details != nil && len(*err.Details) > 0 { + for _, detail := range *err.Details { + if detail.Message != nil { + message = append(message, *detail.Message) + } + if detail.Innererror != nil { + if detail.Innererror.Message != nil { + message = append(message, *detail.Innererror.Message) + } + if detail.Innererror.SeverityName != nil && *detail.Innererror.SeverityName == "Error" { + // Severity names are not documented in the API response format + // https://docs.microsoft.com/en-us/azure/azure-monitor/logs/api/response-format + // so assuming either an error or a warning + severity = data.NoticeSeverityError + } + } + } + } + return data.Notice{ + Severity: severity, + Text: strings.Join(message, " "), + } +} + // ResponseTableToFrame converts an AzureResponseTable to a data.Frame. -func ResponseTableToFrame(table *types.AzureResponseTable) (*data.Frame, error) { +func ResponseTableToFrame(table *types.AzureResponseTable, res AzureLogAnalyticsResponse) (*data.Frame, error) { converterFrame, err := converterFrameForTable(table) if err != nil { return nil, err @@ -25,6 +56,11 @@ func ResponseTableToFrame(table *types.AzureResponseTable) (*data.Frame, error) } } } + + if res.Error != nil { + converterFrame.Frame.AppendNotices(apiErrorToNotice(res.Error)) + } + return converterFrame.Frame, nil } diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame_test.go b/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame_test.go index 281111fbd3d..9cf19dc81b4 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame_test.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-response-table-frame_test.go @@ -139,12 +139,42 @@ func TestLogTableToFrame(t *testing.T) { return frame }, }, + { + name: "data and error in real response", + testFile: "loganalytics/9-log-analytics-response-error.json", + expectedFrame: func() *data.Frame { + frame := data.NewFrame("", + data.NewField("OperationName", nil, []*string{pointer.String("Create or Update Virtual Machine")}), + data.NewField("Level", nil, []*string{pointer.String("Informational")}), + ) + frame.Meta = &data.FrameMeta{ + Custom: &LogAnalyticsMeta{ColumnTypes: []string{"string", "string"}}, + Notices: []data.Notice{{Severity: data.NoticeSeverityError, Text: "There were some errors when processing your query. Something went wrong processing your query on the server. The results of this query exceed the set limit of 1 records."}}, + } + return frame + }, + }, + { + name: "data and warning in real response", + testFile: "loganalytics/10-log-analytics-response-warning.json", + expectedFrame: func() *data.Frame { + frame := data.NewFrame("", + data.NewField("OperationName", nil, []*string{pointer.String("Create or Update Virtual Machine")}), + data.NewField("Level", nil, []*string{pointer.String("Informational")}), + ) + frame.Meta = &data.FrameMeta{ + Custom: &LogAnalyticsMeta{ColumnTypes: []string{"string", "string"}}, + Notices: []data.Notice{{Severity: data.NoticeSeverityWarning, Text: "There were some errors when processing your query. Something went wrong processing your query on the server. Not sure what happened."}}, + } + return frame + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { res := loadLogAnalyticsTestFileWithNumber(t, tt.testFile) - frame, err := ResponseTableToFrame(&res.Tables[0]) + frame, err := ResponseTableToFrame(&res.Tables[0], res) require.NoError(t, err) if diff := cmp.Diff(tt.expectedFrame(), frame, data.FrameTestCompareOptions()...); diff != "" { diff --git a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go index ad26abfaa5e..c39b7b36b33 100644 --- a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go +++ b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go @@ -189,7 +189,7 @@ func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query * return dataResponseErrorWithExecuted(err) } - frame, err := loganalytics.ResponseTableToFrame(&argResponse.Data) + frame, err := loganalytics.ResponseTableToFrame(&argResponse.Data, loganalytics.AzureLogAnalyticsResponse{}) if err != nil { return dataResponseErrorWithExecuted(err) } diff --git a/pkg/tsdb/azuremonitor/testdata/loganalytics/10-log-analytics-response-warning.json b/pkg/tsdb/azuremonitor/testdata/loganalytics/10-log-analytics-response-warning.json new file mode 100644 index 00000000000..151f9235f9d --- /dev/null +++ b/pkg/tsdb/azuremonitor/testdata/loganalytics/10-log-analytics-response-warning.json @@ -0,0 +1,40 @@ +{ + "tables": [ + { + "name": "PrimaryResult", + "columns": [ + { + "name": "OperationName", + "type": "string" + }, + { + "name": "Level", + "type": "string" + } + ], + "rows": [ + [ + "Create or Update Virtual Machine", + "Informational" + ] + ] + } + ], + "error": { + "code": "PartialError", + "message": "There were some errors when processing your query.", + "details": [ + { + "code": "EngineError", + "message": "Something went wrong processing your query on the server.", + "innererror": { + "code": "-2133196797", + "message": "Not sure what happened.", + "severity": 2, + "severityName": "Warning" + } + } + ] + } + } + \ No newline at end of file diff --git a/pkg/tsdb/azuremonitor/testdata/loganalytics/9-log-analytics-response-error.json b/pkg/tsdb/azuremonitor/testdata/loganalytics/9-log-analytics-response-error.json new file mode 100644 index 00000000000..9a449cf2ff0 --- /dev/null +++ b/pkg/tsdb/azuremonitor/testdata/loganalytics/9-log-analytics-response-error.json @@ -0,0 +1,40 @@ +{ + "tables": [ + { + "name": "PrimaryResult", + "columns": [ + { + "name": "OperationName", + "type": "string" + }, + { + "name": "Level", + "type": "string" + } + ], + "rows": [ + [ + "Create or Update Virtual Machine", + "Informational" + ] + ] + } + ], + "error": { + "code": "PartialError", + "message": "There were some errors when processing your query.", + "details": [ + { + "code": "EngineError", + "message": "Something went wrong processing your query on the server.", + "innererror": { + "code": "-2133196797", + "message": "The results of this query exceed the set limit of 1 records.", + "severity": 2, + "severityName": "Error" + } + } + ] + } + } + \ No newline at end of file