Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge: robustperception/azure_metric_exporter master #109

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Docker

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

on:
push:
tags:
- '*.*.*'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Set Tag as env variable
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Build & Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: conplementag/azure_metrics_exporter
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tags: "latest,${{ env.RELEASE_VERSION }}"
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
FROM golang:1.11 as builder
FROM golang:1.16 as builder

WORKDIR /go/src/github.com/RobustPerception/azure_metrics_exporter

COPY . .

RUN make build

FROM quay.io/prometheus/busybox:latest AS app
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ resource_tags:
- "Microsoft.Compute/virtualMachines"
metrics:
- name: "CPU Credits Consumed"
- resource_tag_name: "dbtype"
resource_tag_value: "document"
resource_types:
- Microsoft.DocumentDB/databaseAccounts
metrics:
- name: "TotalRequestUnits"
- name: "TotalRequests"
dimensions: "CollectionName eq \'*\' and StatusCode eq \'*\'"

```

Expand All @@ -106,6 +114,9 @@ When the metric namespace is specified, it will be added as a prefix of the metr
It can be used to target [custom metrics](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-custom-overview), such as [guest OS performance counters](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/collect-custom-metrics-guestos-vm-classic).
If not specified, the default metric namespace of the resource will apply.

The `dimensions` property is optional for all filtering types. If `dimensions` property is provided, it will add the provided dimensions as label in the metrics.
You can get the available `dimensions` for a given resource metrics using [metrics definitions](#retrieving-metric-definitions).

### Resource group filtering

Resources in a resource group can be filtered using the the following keys:
Expand Down
51 changes: 48 additions & 3 deletions azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ type AzureMetricValueResponse struct {
Minimum float64 `json:"minimum"`
Maximum float64 `json:"maximum"`
} `json:"data"`
Dimensions []struct {
Name struct {
LocalizedValue string `json:"localizedValue"`
Value string `json:"value"`
} `json:"name"`
Value string `json:"value"`
} `json:"metadatavalues"`
} `json:"timeseries"`
ID string `json:"id"`
Name struct {
Expand Down Expand Up @@ -279,6 +286,25 @@ func (ac *AzureClient) getMetricDefinitions() (map[string]AzureMetricDefinitionR
definitions[defKey] = *def
}
}
resourcesCache := make(map[string][]byte)
for _, resourceTag := range sc.C.ResourceTags {
resources, err := ac.filteredListByTag(resourceTag, resourcesCache)
if err != nil {
return nil, fmt.Errorf("Failed to get resources for resource tag:value %s:%s and resource types %s: %v",
resourceTag.ResourceTagName, resourceTag.ResourceTagValue, resourceTag.ResourceTypes, err)
}
for _, resource := range resources {
def, err := ac.getAzureMetricDefinitionResponse(resource.ID, resourceTag.MetricNamespace)
if err != nil {
return nil, err
}
defKey := resource.ID
if len(resourceTag.MetricNamespace) > 0 {
defKey = fmt.Sprintf("%s (Metric namespace: %s)", defKey, resourceTag.MetricNamespace)
}
definitions[defKey] = *def
}
}
return definitions, nil
}

Expand Down Expand Up @@ -307,12 +333,27 @@ func (ac *AzureClient) getMetricNamespaces() (map[string]MetricNamespaceCollecti
namespaces[resource.ID] = *namespaceCollection
}
}
resourcesCache := make(map[string][]byte)
for _, resourceTag := range sc.C.ResourceTags {
resources, err := ac.filteredListByTag(resourceTag, resourcesCache)
if err != nil {
return nil, fmt.Errorf("Failed to get resources for resource tag:value %s:%s and resource types %s: %v",
resourceTag.ResourceTagName, resourceTag.ResourceTagValue, resourceTag.ResourceTypes, err)
}
for _, resource := range resources {
namespaceCollection, err := ac.getMetricNamespaceCollectionResponse(resource.ID)
if err != nil {
return nil, err
}
namespaces[resource.ID] = *namespaceCollection
}
}
return namespaces, nil
}

// Returns AzureMetricDefinitionResponse for a given resource
func (ac *AzureClient) getAzureMetricDefinitionResponse(resource string, metricNamespace string) (*AzureMetricDefinitionResponse, error) {
apiVersion := "2018-01-01"
apiVersion := "2019-07-01"

metricsResource := fmt.Sprintf("subscriptions/%s%s", sc.C.Credentials.SubscriptionID, resource)
metricsTarget := fmt.Sprintf("%s/%s/providers/microsoft.insights/metricDefinitions?api-version=%s", sc.C.ResourceManagerURL, metricsResource, apiVersion)
Expand Down Expand Up @@ -584,8 +625,8 @@ type batchRequest struct {
Method string `json:"httpMethod"`
}

func resourceURLFrom(resource string, metricNamespace string, metricNames string, aggregations []string) string {
apiVersion := "2018-01-01"
func resourceURLFrom(resource string, metricNamespace string, metricNames string, aggregations []string, dimensions string) string {
apiVersion := "2019-07-01"

path := fmt.Sprintf(
"/subscriptions/%s%s/providers/microsoft.insights/metrics",
Expand All @@ -602,6 +643,9 @@ func resourceURLFrom(resource string, metricNamespace string, metricNames string
if metricNamespace != "" {
values.Add("metricnamespace", metricNamespace)
}
if dimensions != "" {
values.Add("$filter", dimensions)
}
filtered := filterAggregations(aggregations)
values.Add("aggregation", strings.Join(filtered, ","))
values.Add("timespan", fmt.Sprintf("%s/%s", startTime, endTime))
Expand All @@ -611,6 +655,7 @@ func resourceURLFrom(resource string, metricNamespace string, metricNames string
Path: path,
RawQuery: values.Encode(),
}

return url.String()
}

Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ type Target struct {
MetricNamespace string `yaml:"metric_namespace"`
Metrics []Metric `yaml:"metrics"`
Aggregations []string `yaml:"aggregations"`
Dimensions string `yaml:"dimensions"`

XXX map[string]interface{} `yaml:",inline"`
}
Expand All @@ -162,6 +163,7 @@ type ResourceGroup struct {
ResourceNameExcludeRe []Regexp `yaml:"resource_name_exclude_re"`
Metrics []Metric `yaml:"metrics"`
Aggregations []string `yaml:"aggregations"`
Dimensions string `yaml:"dimensions"`

XXX map[string]interface{} `yaml:",inline"`
}
Expand All @@ -174,6 +176,7 @@ type ResourceTag struct {
ResourceTypes []string `yaml:"resource_types"`
Metrics []Metric `yaml:"metrics"`
Aggregations []string `yaml:"aggregations"`
Dimensions string `yaml:"dimensions"`

XXX map[string]interface{} `yaml:",inline"`
}
Expand Down
Loading