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

Azure Monitor: fix metric timespan to restore Storage Account PT1H metrics #40367

Merged
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff]
- Add GCP 'instance_id' resource label in ECS cloud fields. {issue}40033[40033] {pull}40062[40062]
- Fix missing metrics from CloudWatch when include_linked_accounts set to false. {issue}40071[40071] {pull}40135[40135]
- Update beat module with apm-server monitoring metrics fields {pull}40127[40127]
- Fix Azure Monitor metric timespan to restore Storage Account PT1H metrics {issue}40376[40376] {pull}40367[40367]

*Osquerybeat*

Expand Down
1 change: 0 additions & 1 deletion x-pack/metricbeat/module/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error {
//
// See "Round outer limits" and "Round inner limits" tests in
// the metric_registry_test.go for more information.
//referenceTime := time.Now().UTC().Round(time.Second)
referenceTime := time.Now().UTC()

// Initialize cloud resources and monitor metrics
Expand Down
89 changes: 80 additions & 9 deletions x-pack/metricbeat/module/azure/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"

"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor"

"github.com/elastic/beats/v7/metricbeat/mb"
"github.com/elastic/elastic-agent-libs/logp"
)
Expand Down Expand Up @@ -119,19 +120,89 @@ func (client *Client) InitResources(fn mapResourceMetrics) error {
return nil
}

// buildTimespan returns the timespan for the metric values given the reference time,
// time grain and collection period.
//
// (1) When the collection period is greater than the time grain, the timespan
// will be:
//
// | time grain
// │ │◀──(PT1M)──▶ │
// │ │
// ├──────────────────────────────────────────┼─────────────┼─────────────
// │ │
// │ timespan │ │
// |◀───────────────────────(5min)─────────────────────────▶│
// │ │ │
// | period │
// │◀───────────────────────(5min)────────────┼────────────▶│
// │ │
// │ │ │
// | │
// | Now
// | │
//
// In this case, the API will return five metric values, because
// the time grain is 1 minute and the timespan is 5 minutes.
//
// (2) When the collection period is equal to the time grain,
// the timespan will be:
//
// |
// │ time grain │
// |◀───────────────────────(5min)─────────────────────────▶│
// │ │
// ├────────────────────────────────────────────────────────┼─────────────
// │ │
// │ timespan │
// |◀───────────────────────(5min)─────────────────────────▶│
// │ │
// | period │
// │◀───────────────────────(5min)─────────────────────────▶│
// │ │
// │ │
// | │
// | Now
// | │
//
// In this case, the API will return one metric value.
//
// (3) When the collection period is less than the time grain,
// the timespan will be:
//
// | period
// │ │◀──(5min)──▶ │
// │ │
// ├──────────────────────────────────────────┼─────────────┼─────────────
// │ │
// │ timespan │ │
// |◀───────────────────────(60min)────────────────────────▶│
// │ │ │
// | time grain │
// │◀───────────────────────(PT1H)────────────┼────────────▶│
// │ │
// │ │ │
// | Now
// | │
// |
//
// In this case, the API will return one metric value.
func buildTimespan(referenceTime time.Time, timeGrain string, collectionPeriod time.Duration) string {
timespanDuration := max(asDuration(timeGrain), collectionPeriod)

endTime := referenceTime
startTime := endTime.Add(timespanDuration * -1)

return fmt.Sprintf("%s/%s", startTime.Format(time.RFC3339), endTime.Format(time.RFC3339))
}

// GetMetricValues returns the metric values for the given cloud resources.
func (client *Client) GetMetricValues(referenceTime time.Time, metrics []Metric, reporter mb.ReporterV2) []Metric {
var result []Metric

// Same end time for all metrics in the same batch.
interval := client.Config.Period

// Fetch in the range [{-2 x INTERVAL},{-1 x INTERVAL}) with a delay of {INTERVAL}.
endTime := referenceTime.Add(interval * (-1))
startTime := endTime.Add(interval * (-1))
timespan := fmt.Sprintf("%s/%s", startTime.Format(time.RFC3339), endTime.Format(time.RFC3339))

for _, metric := range metrics {
timespan := buildTimespan(referenceTime, metric.TimeGrain, client.Config.Period)

//
// Before fetching the metric values, check if the metric
// has been collected within the time grain.
Expand Down
53 changes: 53 additions & 0 deletions x-pack/metricbeat/module/azure/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,56 @@ func TestGetMetricValues(t *testing.T) {
m.AssertExpectations(t)
})
}

func TestBuildBuildTimespan(t *testing.T) {
t.Run("Collection period greater than the time grain (PT1M metric every 5 minutes)", func(t *testing.T) {
referenceTime, _ := time.Parse(time.RFC3339, "2024-07-30T18:56:00Z")
timeGain := "PT1M"
collectionPeriod := 5 * time.Minute

timespan := buildTimespan(referenceTime, timeGain, collectionPeriod)

assert.Equal(t, "2024-07-30T18:51:00Z/2024-07-30T18:56:00Z", timespan)
})

t.Run("Collection period equal to time grain (PT1M metric every 1 minutes)", func(t *testing.T) {
referenceTime, _ := time.Parse(time.RFC3339, "2024-07-30T18:56:00Z")
timeGain := "PT1M"
collectionPeriod := 1 * time.Minute

timespan := buildTimespan(referenceTime, timeGain, collectionPeriod)

assert.Equal(t, "2024-07-30T18:55:00Z/2024-07-30T18:56:00Z", timespan)
})

t.Run("Collection period equal to time grain (PT5M metric every 5 minutes)", func(t *testing.T) {
referenceTime, _ := time.Parse(time.RFC3339, "2024-07-30T18:56:00Z")
timeGain := "PT5M"
collectionPeriod := 5 * time.Minute

timespan := buildTimespan(referenceTime, timeGain, collectionPeriod)

assert.Equal(t, "2024-07-30T18:51:00Z/2024-07-30T18:56:00Z", timespan)
})

t.Run("Collection period equal to time grain (PT1H metric every 60 minutes)", func(t *testing.T) {
referenceTime, _ := time.Parse(time.RFC3339, "2024-07-30T18:56:00Z")
timeGain := "PT1H"
collectionPeriod := 60 * time.Minute

timespan := buildTimespan(referenceTime, timeGain, collectionPeriod)

assert.Equal(t, "2024-07-30T17:56:00Z/2024-07-30T18:56:00Z", timespan)
})

t.Run("Collection period is less that time grain (PT1H metric every 5 minutes)", func(t *testing.T) {
referenceTime, _ := time.Parse(time.RFC3339, "2024-07-30T18:56:00Z")
timeGain := "PT1H"
collectionPeriod := 5 * time.Minute

timespan := buildTimespan(referenceTime, timeGain, collectionPeriod)

assert.Equal(t, "2024-07-30T17:56:00Z/2024-07-30T18:56:00Z", timespan)
})

}
Loading