Azure Monitor: Add logs query builder (#99055)
This commit is contained in:
@@ -28,6 +28,18 @@ import (
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/utils"
|
||||
)
|
||||
|
||||
// Returns tables with the `HasData` field set to true
|
||||
func filterTablesWithData(tables []types.MetadataTable) []types.MetadataTable {
|
||||
filtered := []types.MetadataTable{}
|
||||
for _, table := range tables {
|
||||
if table.HasData {
|
||||
filtered = append(filtered, table)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (e *AzureLogAnalyticsDatasource) ResourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client) (http.ResponseWriter, error) {
|
||||
if req.URL.Path == "/usage/basiclogs" {
|
||||
newUrl := &url.URL{
|
||||
@@ -36,7 +48,57 @@ func (e *AzureLogAnalyticsDatasource) ResourceRequest(rw http.ResponseWriter, re
|
||||
Path: "/v1/query",
|
||||
}
|
||||
return e.GetBasicLogsUsage(req.Context(), newUrl.String(), cli, rw, req.Body)
|
||||
} else if strings.Contains(req.URL.Path, "/metadata") {
|
||||
// Add necessary headers
|
||||
req.Header.Set("Prefer", "metadata-format-v4,exclude-resourcetypes,exclude-customfunctions")
|
||||
queryParams := req.URL.Query()
|
||||
// Add necessary query params
|
||||
queryParams.Add("select", "categories,solutions,tables,workspaces")
|
||||
req.URL.RawQuery = queryParams.Encode()
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch metadata: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
e.Logger.Warn("Failed to close response body for metadata request", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
encoding := resp.Header.Get("Content-Encoding")
|
||||
body, err := decode(encoding, resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read metadata response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("metadata API error: %s", string(body))
|
||||
}
|
||||
|
||||
var metadata types.AzureLogAnalyticsMetadata
|
||||
err = json.Unmarshal(body, &metadata)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal metadata response: %w", err)
|
||||
}
|
||||
metadata.Tables = filterTablesWithData(metadata.Tables)
|
||||
|
||||
responseBody, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal metadata response: %w", err)
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err = rw.Write(responseBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write metadata response: %w", err)
|
||||
}
|
||||
|
||||
return rw, nil
|
||||
}
|
||||
|
||||
// Default behavior for other requests
|
||||
return e.Proxy.Do(rw, req, cli)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package loganalytics
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/andybalholm/brotli"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery"
|
||||
@@ -136,3 +140,40 @@ func ConvertTime(timeStamp string) (time.Time, error) {
|
||||
func GetDataVolumeRawQuery(table string) string {
|
||||
return fmt.Sprintf("Usage \n| where DataType == \"%s\"\n| where IsBillable == true\n| summarize BillableDataGB = round(sum(Quantity) / 1000, 3)", table)
|
||||
}
|
||||
|
||||
// This function handles various compression mechanisms that may have been used on a response body
|
||||
func decode(encoding string, original io.ReadCloser) ([]byte, error) {
|
||||
var reader io.Reader
|
||||
var err error
|
||||
switch encoding {
|
||||
case "gzip":
|
||||
reader, err = gzip.NewReader(original)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := reader.(io.ReadCloser).Close(); err != nil {
|
||||
backend.Logger.Warn("Failed to close reader body", "err", err)
|
||||
}
|
||||
}()
|
||||
case "deflate":
|
||||
reader = flate.NewReader(original)
|
||||
defer func() {
|
||||
if err := reader.(io.ReadCloser).Close(); err != nil {
|
||||
backend.Logger.Warn("Failed to close reader body", "err", err)
|
||||
}
|
||||
}()
|
||||
case "br":
|
||||
reader = brotli.NewReader(original)
|
||||
case "":
|
||||
reader = original
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected encoding type %v", err)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user