mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Parse non-fatal errors for Logs (#51320)
This commit is contained in:
parent
9e80e44b45
commit
b10ddfdf8c
@ -148,7 +148,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A
|
|||||||
|
|
||||||
// If azureLogAnalyticsSameAs is defined and set to false, return an error
|
// If azureLogAnalyticsSameAs is defined and set to false, return an error
|
||||||
if sameAs, ok := dsInfo.JSONData["azureLogAnalyticsSameAs"]; ok && !sameAs.(bool) {
|
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)
|
req, err := e.createRequest(ctx, dsInfo, url)
|
||||||
@ -187,7 +187,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A
|
|||||||
return dataResponseErrorWithExecuted(err)
|
return dataResponseErrorWithExecuted(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
frame, err := ResponseTableToFrame(t)
|
frame, err := ResponseTableToFrame(t, logResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dataResponseErrorWithExecuted(err)
|
return dataResponseErrorWithExecuted(err)
|
||||||
}
|
}
|
||||||
@ -234,9 +234,31 @@ func (e *AzureLogAnalyticsDatasource) createRequest(ctx context.Context, dsInfo
|
|||||||
return req, nil
|
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.
|
// AzureLogAnalyticsResponse is the json response object from the Azure Log Analytics API.
|
||||||
type AzureLogAnalyticsResponse struct {
|
type AzureLogAnalyticsResponse struct {
|
||||||
Tables []types.AzureResponseTable `json:"tables"`
|
Tables []types.AzureResponseTable `json:"tables"`
|
||||||
|
Error *AzureLogAnalyticsAPIError `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPrimaryResultTable returns the first table in the response named "PrimaryResult", or an
|
// GetPrimaryResultTable returns the first table in the response named "PrimaryResult", or an
|
||||||
|
@ -235,7 +235,7 @@ func Test_executeQueryErrorWithDifferentLogAnalyticsCreds(t *testing.T) {
|
|||||||
if res.Error == nil {
|
if res.Error == nil {
|
||||||
t.Fatal("expecting an error")
|
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")
|
t.Error("expecting the error to inform of bad credentials")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,45 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
"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.
|
// 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)
|
converterFrame, err := converterFrameForTable(table)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
return converterFrame.Frame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,12 +139,42 @@ func TestLogTableToFrame(t *testing.T) {
|
|||||||
return frame
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
res := loadLogAnalyticsTestFileWithNumber(t, tt.testFile)
|
res := loadLogAnalyticsTestFileWithNumber(t, tt.testFile)
|
||||||
frame, err := ResponseTableToFrame(&res.Tables[0])
|
frame, err := ResponseTableToFrame(&res.Tables[0], res)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if diff := cmp.Diff(tt.expectedFrame(), frame, data.FrameTestCompareOptions()...); diff != "" {
|
if diff := cmp.Diff(tt.expectedFrame(), frame, data.FrameTestCompareOptions()...); diff != "" {
|
||||||
|
@ -188,7 +188,7 @@ func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query *
|
|||||||
return dataResponseErrorWithExecuted(err)
|
return dataResponseErrorWithExecuted(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
frame, err := loganalytics.ResponseTableToFrame(&argResponse.Data)
|
frame, err := loganalytics.ResponseTableToFrame(&argResponse.Data, loganalytics.AzureLogAnalyticsResponse{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dataResponseErrorWithExecuted(err)
|
return dataResponseErrorWithExecuted(err)
|
||||||
}
|
}
|
||||||
|
40
pkg/tsdb/azuremonitor/testdata/loganalytics/10-log-analytics-response-warning.json
vendored
Normal file
40
pkg/tsdb/azuremonitor/testdata/loganalytics/10-log-analytics-response-warning.json
vendored
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
40
pkg/tsdb/azuremonitor/testdata/loganalytics/9-log-analytics-response-error.json
vendored
Normal file
40
pkg/tsdb/azuremonitor/testdata/loganalytics/9-log-analytics-response-error.json
vendored
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user